Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 52 additions & 36 deletions hyperdrive/packages/app-store/ui/src/utils/predictTBA.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,94 @@
import { encodePacked, keccak256, getAddress } from 'viem';
import { encodePacked, keccak256, getAddress, encodeAbiParameters, type Address, type Hex } from 'viem';
import { hyperhash } from './hyperhash';

const ERC6551_REGISTRY = '0x000000006551c19487814612e58FE06813775758' as const;

export function predictTBAAddress(
hypermapAddr: `0x${string}`,
hypermapAddr: Address,
label: string,
chainId: number = 8453 // Base chain ID
): `0x${string}` {
): Address {
// Calculate the namehash for the label
const namehash = hyperhash(label);

// First compute the proxy address
const proxyAddr = computeProxyAddress(hypermapAddr, hypermapAddr, namehash);
console.log("proxyAddr", proxyAddr);
return computeAccount(proxyAddr, namehash, BigInt(chainId), hypermapAddr, BigInt(namehash));
}

// Implementation bytecode hash for ERC6551 v3
const ACCOUNT_IMPLEMENTATION_BYTECODE_HASH = keccak256(encodePacked(['string'], ['erc6551:v3:account']));
function computeAccount(
implementation: Address,
salt: bigint | `0x${string}`,
chainId: bigint,
tokenContract: Address,
tokenId: bigint
): Address {
// ERC-1167 minimal proxy bytecode components
const fullHeader = "0x3d60ad80600a3d3981f3363d3d373d3d3d363d73" as Hex;
const footer = "0x5af43d82803e903d91602b57fd5bf3" as Hex;

// Create the init code for ERC6551 account
const initCode = encodePacked(
['bytes10', 'address', 'bytes32', 'uint256', 'address', 'uint256'],
const bytecode = encodePacked(
["bytes", "address", "bytes"],
[fullHeader, implementation, footer]
);

// Encode the constructor arguments (salt, chainId, tokenContract, tokenId)
const constructorArgs = encodeAbiParameters(
[
'0x' + '00'.repeat(10), // 10 bytes of zeros for ERC6551 v3
proxyAddr, // implementation (proxy)
namehash, // salt
BigInt(chainId), // chainId
hypermapAddr, // tokenContract
BigInt(namehash) // tokenId (using namehash as tokenId)
]
{ type: "bytes32" },
{ type: "uint256" },
{ type: "address" },
{ type: "uint256" },
],
[salt as `0x${string}`, chainId, tokenContract, tokenId]
);

// Compute init code hash for ERC6551 account
const initCodeHash = keccak256(
encodePacked(
['bytes', 'bytes32'],
[initCode, ACCOUNT_IMPLEMENTATION_BYTECODE_HASH]
)
// Combine bytecode with constructor arguments to match the exact memory layout
const initCode = encodePacked(
["bytes", "bytes"],
[bytecode, constructorArgs]
);

// Compute the TBA address using CREATE2
const hash = keccak256(
// CREATE2 formula
const create2Hash = keccak256(
encodePacked(
['bytes1', 'address', 'bytes32'],
['0xff', ERC6551_REGISTRY, initCodeHash]
["bytes1", "address", "bytes32", "bytes32"],
["0xff" as Hex, ERC6551_REGISTRY, salt as `0x${string}`, keccak256(initCode)]
)
);

return getAddress(`0x${hash.slice(-40)}`) as `0x${string}`;
return getAddress(`0x${create2Hash.slice(-40)}`);

}


function computeProxyAddress(
deployer: `0x${string}`,
hypermapAddr: `0x${string}`,
salt: `0x${string}`
): `0x${string}` {
deployer: Address,
hypermapAddr: Address,
salt: string
): Address {
// HyperAccountProxy creation code with constructor argument
const PROXY_CREATION_CODE = '0x60a0604052348015600e575f5ffd5b5060405161051d38038061051d833981016040819052602b91603b565b6001600160a01b03166080526066565b5f60208284031215604a575f5ffd5b81516001600160a01b0381168114605f575f5ffd5b9392505050565b6080516104a061007d5f395f607a01526104a05ff3fe608060405260043610610021575f3560e01c8063d1f578941461003257610028565b3661002857005b610030610045565b005b610030610040366004610383565b610057565b610055610050610132565b610169565b565b7f7d0893b5fe6077fb4cf083ec3487b8eece7e03b4ab6e888f7a8a1758010f8c007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146100df57805460ff16156100bf576100ba610045565b6100df565b60405163572190d160e01b81523360048201526024015b60405180910390fd5b805460ff16156101015760405162dc149f60e41b815260040160405180910390fd5b5f61010a610132565b6001600160a01b03160361012d57805460ff1916600117815561012d8383610187565b505050565b5f6101647f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e808015610183573d5ff35b3d5ffd5b610190826101e0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101d45761012d8282610256565b6101dc6102c8565b5050565b806001600160a01b03163b5f0361021557604051634c9c8ce360e01b81526001600160a01b03821660048201526024016100d6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516102729190610454565b5f60405180830381855af49150503d805f81146102aa576040519150601f19603f3d011682016040523d82523d5f602084013e6102af565b606091505b50915091506102bf8583836102e7565b95945050505050565b34156100555760405163b398979f60e01b815260040160405180910390fd5b6060826102fc576102f782610346565b61033f565b815115801561031357506001600160a01b0384163b155b1561033c57604051639996b31560e01b81526001600160a01b03851660048201526024016100d6565b50805b9392505050565b8051156103565780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610394575f5ffd5b82356001600160a01b03811681146103aa575f5ffd5b9150602083013567ffffffffffffffff8111156103c5575f5ffd5b8301601f810185136103d5575f5ffd5b803567ffffffffffffffff8111156103ef576103ef61036f565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561041e5761041e61036f565b604052818152828201602001871015610435575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f82518060208501845e5f92019182525091905056fea264697066735822122014ea9f00459f92ee24210cb4740336c93386a9e0bc81a68800b4e50d397e23ab64736f6c634300081c0033';
const PROXY_CREATION_CODE = '0x60a0604052348015600e575f5ffd5b5060405161051d38038061051d833981016040819052602b91603b565b6001600160a01b03166080526066565b5f60208284031215604a575f5ffd5b81516001600160a01b0381168114605f575f5ffd5b9392505050565b6080516104a061007d5f395f607a01526104a05ff3fe608060405260043610610021575f3560e01c8063d1f578941461003257610028565b3661002857005b610030610045565b005b610030610040366004610383565b610057565b610055610050610132565b610169565b565b7f7d0893b5fe6077fb4cf083ec3487b8eece7e03b4ab6e888f7a8a1758010f8c007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146100df57805460ff16156100bf576100ba610045565b6100df565b60405163572190d160e01b81523360048201526024015b60405180910390fd5b805460ff16156101015760405162dc149f60e41b815260040160405180910390fd5b5f61010a610132565b6001600160a01b03160361012d57805460ff1916600117815561012d8383610187565b505050565b5f6101647f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e808015610183573d5ff35b3d5ffd5b610190826101e0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101d45761012d8282610256565b6101dc6102c8565b5050565b806001600160a01b03163b5f0361021557604051634c9c8ce360e01b81526001600160a01b03821660048201526024016100d6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516102729190610454565b5f60405180830381855af49150503d805f81146102aa576040519150601f19603f3d011682016040523d82523d5f602084013e6102af565b606091505b50915091506102bf8583836102e7565b95945050505050565b34156100555760405163b398979f60e01b815260040160405180910390fd5b6060826102fc576102f782610346565b61033f565b815115801561031357506001600160a01b0384163b155b1561033c57604051639996b31560e01b81526001600160a01b03851660048201526024016100d6565b50805b9392505050565b8051156103565780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610394575f5ffd5b82356001600160a01b03811681146103aa575f5ffd5b9150602083013567ffffffffffffffff8111156103c5575f5ffd5b8301601f810185136103d5575f5ffd5b803567ffffffffffffffff8111156103ef576103ef61036f565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561041e5761041e61036f565b604052818152828201602001871015610435575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f82518060208501845e5f92019182525091905056fea26469706673582212205c8437c90a52b26afb62a6e21b8baa0d106dcc547054521f0074dea229fd630f64736f6c634300081c0033';

const proxyCreationCodeHash = keccak256(
encodePacked(
['bytes', 'bytes'],
[
PROXY_CREATION_CODE,
encodePacked(['address'], [hypermapAddr])
encodeAbiParameters(
[{ type: 'address' }],
[getAddress(hypermapAddr)]
)
]
)
);

const hash = keccak256(
encodePacked(
['bytes1', 'address', 'bytes32', 'bytes32'],
['0xff', deployer, salt, proxyCreationCodeHash]
['0xff', deployer, salt as `0x${string}`, proxyCreationCodeHash]
)
);

return getAddress(`0x${hash.slice(-40)}`) as `0x${string}`;
}
return getAddress(`0x${hash.slice(-40)}`) as Address;
}
88 changes: 52 additions & 36 deletions hyperdrive/src/register-ui/src/utils/predictTBA.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,94 @@
import { encodePacked, keccak256, getAddress } from 'viem';
import { encodePacked, keccak256, getAddress, encodeAbiParameters, type Address, type Hex } from 'viem';
import { hyperhash } from './hyperhash';

const ERC6551_REGISTRY = '0x000000006551c19487814612e58FE06813775758' as const;

export function predictTBAAddress(
hypermapAddr: `0x${string}`,
hypermapAddr: Address,
label: string,
chainId: number = 8453 // Base chain ID
): `0x${string}` {
): Address {
// Calculate the namehash for the label
const namehash = hyperhash(label);

// First compute the proxy address
const proxyAddr = computeProxyAddress(hypermapAddr, hypermapAddr, namehash);
console.log("proxyAddr", proxyAddr);
return computeAccount(proxyAddr, namehash, BigInt(chainId), hypermapAddr, BigInt(namehash));
}

// Implementation bytecode hash for ERC6551 v3
const ACCOUNT_IMPLEMENTATION_BYTECODE_HASH = keccak256(encodePacked(['string'], ['erc6551:v3:account']));
function computeAccount(
implementation: Address,
salt: bigint | `0x${string}`,
chainId: bigint,
tokenContract: Address,
tokenId: bigint
): Address {
// ERC-1167 minimal proxy bytecode components
const fullHeader = "0x3d60ad80600a3d3981f3363d3d373d3d3d363d73" as Hex;
const footer = "0x5af43d82803e903d91602b57fd5bf3" as Hex;

// Create the init code for ERC6551 account
const initCode = encodePacked(
['bytes10', 'address', 'bytes32', 'uint256', 'address', 'uint256'],
const bytecode = encodePacked(
["bytes", "address", "bytes"],
[fullHeader, implementation, footer]
);

// Encode the constructor arguments (salt, chainId, tokenContract, tokenId)
const constructorArgs = encodeAbiParameters(
[
'0x' + '00'.repeat(10), // 10 bytes of zeros for ERC6551 v3
proxyAddr, // implementation (proxy)
namehash, // salt
BigInt(chainId), // chainId
hypermapAddr, // tokenContract
BigInt(namehash) // tokenId (using namehash as tokenId)
]
{ type: "bytes32" },
{ type: "uint256" },
{ type: "address" },
{ type: "uint256" },
],
[salt as `0x${string}`, chainId, tokenContract, tokenId]
);

// Compute init code hash for ERC6551 account
const initCodeHash = keccak256(
encodePacked(
['bytes', 'bytes32'],
[initCode, ACCOUNT_IMPLEMENTATION_BYTECODE_HASH]
)
// Combine bytecode with constructor arguments to match the exact memory layout
const initCode = encodePacked(
["bytes", "bytes"],
[bytecode, constructorArgs]
);

// Compute the TBA address using CREATE2
const hash = keccak256(
// CREATE2 formula
const create2Hash = keccak256(
encodePacked(
['bytes1', 'address', 'bytes32'],
['0xff', ERC6551_REGISTRY, initCodeHash]
["bytes1", "address", "bytes32", "bytes32"],
["0xff" as Hex, ERC6551_REGISTRY, salt as `0x${string}`, keccak256(initCode)]
)
);

return getAddress(`0x${hash.slice(-40)}`) as `0x${string}`;
return getAddress(`0x${create2Hash.slice(-40)}`);

}


function computeProxyAddress(
deployer: `0x${string}`,
hypermapAddr: `0x${string}`,
salt: `0x${string}`
): `0x${string}` {
deployer: Address,
hypermapAddr: Address,
salt: string
): Address {
// HyperAccountProxy creation code with constructor argument
const PROXY_CREATION_CODE = '0x60a0604052348015600e575f5ffd5b5060405161051d38038061051d833981016040819052602b91603b565b6001600160a01b03166080526066565b5f60208284031215604a575f5ffd5b81516001600160a01b0381168114605f575f5ffd5b9392505050565b6080516104a061007d5f395f607a01526104a05ff3fe608060405260043610610021575f3560e01c8063d1f578941461003257610028565b3661002857005b610030610045565b005b610030610040366004610383565b610057565b610055610050610132565b610169565b565b7f7d0893b5fe6077fb4cf083ec3487b8eece7e03b4ab6e888f7a8a1758010f8c007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146100df57805460ff16156100bf576100ba610045565b6100df565b60405163572190d160e01b81523360048201526024015b60405180910390fd5b805460ff16156101015760405162dc149f60e41b815260040160405180910390fd5b5f61010a610132565b6001600160a01b03160361012d57805460ff1916600117815561012d8383610187565b505050565b5f6101647f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e808015610183573d5ff35b3d5ffd5b610190826101e0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101d45761012d8282610256565b6101dc6102c8565b5050565b806001600160a01b03163b5f0361021557604051634c9c8ce360e01b81526001600160a01b03821660048201526024016100d6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516102729190610454565b5f60405180830381855af49150503d805f81146102aa576040519150601f19603f3d011682016040523d82523d5f602084013e6102af565b606091505b50915091506102bf8583836102e7565b95945050505050565b34156100555760405163b398979f60e01b815260040160405180910390fd5b6060826102fc576102f782610346565b61033f565b815115801561031357506001600160a01b0384163b155b1561033c57604051639996b31560e01b81526001600160a01b03851660048201526024016100d6565b50805b9392505050565b8051156103565780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610394575f5ffd5b82356001600160a01b03811681146103aa575f5ffd5b9150602083013567ffffffffffffffff8111156103c5575f5ffd5b8301601f810185136103d5575f5ffd5b803567ffffffffffffffff8111156103ef576103ef61036f565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561041e5761041e61036f565b604052818152828201602001871015610435575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f82518060208501845e5f92019182525091905056fea264697066735822122014ea9f00459f92ee24210cb4740336c93386a9e0bc81a68800b4e50d397e23ab64736f6c634300081c0033';
const PROXY_CREATION_CODE = '0x60a0604052348015600e575f5ffd5b5060405161051d38038061051d833981016040819052602b91603b565b6001600160a01b03166080526066565b5f60208284031215604a575f5ffd5b81516001600160a01b0381168114605f575f5ffd5b9392505050565b6080516104a061007d5f395f607a01526104a05ff3fe608060405260043610610021575f3560e01c8063d1f578941461003257610028565b3661002857005b610030610045565b005b610030610040366004610383565b610057565b610055610050610132565b610169565b565b7f7d0893b5fe6077fb4cf083ec3487b8eece7e03b4ab6e888f7a8a1758010f8c007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633146100df57805460ff16156100bf576100ba610045565b6100df565b60405163572190d160e01b81523360048201526024015b60405180910390fd5b805460ff16156101015760405162dc149f60e41b815260040160405180910390fd5b5f61010a610132565b6001600160a01b03160361012d57805460ff1916600117815561012d8383610187565b505050565b5f6101647f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e808015610183573d5ff35b3d5ffd5b610190826101e0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101d45761012d8282610256565b6101dc6102c8565b5050565b806001600160a01b03163b5f0361021557604051634c9c8ce360e01b81526001600160a01b03821660048201526024016100d6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516102729190610454565b5f60405180830381855af49150503d805f81146102aa576040519150601f19603f3d011682016040523d82523d5f602084013e6102af565b606091505b50915091506102bf8583836102e7565b95945050505050565b34156100555760405163b398979f60e01b815260040160405180910390fd5b6060826102fc576102f782610346565b61033f565b815115801561031357506001600160a01b0384163b155b1561033c57604051639996b31560e01b81526001600160a01b03851660048201526024016100d6565b50805b9392505050565b8051156103565780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610394575f5ffd5b82356001600160a01b03811681146103aa575f5ffd5b9150602083013567ffffffffffffffff8111156103c5575f5ffd5b8301601f810185136103d5575f5ffd5b803567ffffffffffffffff8111156103ef576103ef61036f565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561041e5761041e61036f565b604052818152828201602001871015610435575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f82518060208501845e5f92019182525091905056fea26469706673582212205c8437c90a52b26afb62a6e21b8baa0d106dcc547054521f0074dea229fd630f64736f6c634300081c0033';

const proxyCreationCodeHash = keccak256(
encodePacked(
['bytes', 'bytes'],
[
PROXY_CREATION_CODE,
encodePacked(['address'], [hypermapAddr])
encodeAbiParameters(
[{ type: 'address' }],
[getAddress(hypermapAddr)]
)
]
)
);

const hash = keccak256(
encodePacked(
['bytes1', 'address', 'bytes32', 'bytes32'],
['0xff', deployer, salt, proxyCreationCodeHash]
['0xff', deployer, salt as `0x${string}`, proxyCreationCodeHash]
)
);

return getAddress(`0x${hash.slice(-40)}`) as `0x${string}`;
}
return getAddress(`0x${hash.slice(-40)}`) as Address;
}