-
Notifications
You must be signed in to change notification settings - Fork 559
[WIP] ERC20 assets #7321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
kumaryash90
wants to merge
10
commits into
main
Choose a base branch
from
yash/ocr-contracts-integration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+4,551
−52
Draft
[WIP] ERC20 assets #7321
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
376d184
generate entrypoint functions
kumaryash90 2253ce5
Merge branch 'main' into yash/ocr-contracts-integration
MananTank 6b816fb
Fix lint errors
MananTank 7266b31
update package.json
MananTank 47c87ea
remove export *
MananTank 82cc116
remove export *
MananTank f44e15c
Merge branch 'main' into yash/ocr-contracts-integration
MananTank 873e46d
base mainnet entrypoint
kumaryash90 b716941
fix entrypoint addr
kumaryash90 0d71393
fix lint
MananTank File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"thirdweb": patch | ||
--- | ||
|
||
ERC20 assets |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
packages/thirdweb/scripts/generate/abis/assets/AssetEntrypointERC20.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
[ | ||
"function initialize(address _owner, address _router, address _rewardLocker)", | ||
"function setRouter(address router)", | ||
"function getRouter() external view returns (address router)", | ||
"function setRewardLocker(address rewardLocker)", | ||
"function getRewardLocker() external view returns (address rewardLocker)", | ||
"function addImplementation((bytes32 contractId, address implementation, uint8 implementationType, uint8 createHook, bytes createHookData) config, bool isDefault)", | ||
"function getImplementation(bytes32 contractId) external view returns ((bytes32 contractId, address implementation, uint8 implementationType, uint8 createHook, bytes createHookData))", | ||
"function createAsset(address creator, (uint256 amount, address referrer, bytes32 salt, bytes data, bytes hookData) createParams) external returns (address asset)", | ||
"function createAssetById(bytes32 contractId, address creator, (uint256 amount, address referrer, bytes32 salt, bytes data, bytes hookData) params) public returns (address asset)", | ||
"function createAssetByImplementationConfig((bytes32 contractId, address implementation, uint8 implementationType, uint8 createHook, bytes createHookData) config, address creator, (uint256 amount, address referrer, bytes32 salt, bytes data, bytes hookData) params) external returns (address asset)", | ||
"function buyAsset(address asset, (address recipient, address referrer, address tokenIn, uint256 amountIn, uint256 minAmountOut, uint256 deadline, bytes data) params) external payable returns (uint256 amountIn, uint256 amountOut)", | ||
"function sellAsset(address asset, (address recipient, address tokenOut, uint256 amountIn, uint256 minAmountOut, uint256 deadline, bytes data) params) external returns (uint256 amountIn, uint256 amountOut)", | ||
"function listAsset(address asset, (address tokenIn, uint256 price, uint256 duration, bytes data) params) external", | ||
"function distributeAsset(address asset, (uint256 amount, address recipient)[] contents) external payable", | ||
"event ImplementationAdded(bytes32 contractId, address indexed implementation, uint8 implementationType, uint8 createHook, bytes32 createHookData)", | ||
"event RouterUpdated(address indexed router)", | ||
"event RewardLockerUpdated(address indexed locker)", | ||
"event AssetCreated(bytes32 contractId, address indexed creator, address indexed asset, address referrer, bytes aux)", | ||
"event AssetDistributed(address asset, uint256 recipientCount, uint256 totalAmount)", | ||
"error InvalidValue()", | ||
"error InvalidContractId()", | ||
"error ValueTransferFailed()", | ||
"error ArrayLengthMismatch()", | ||
"error AssetNotRegistered()", | ||
"error InvalidCreator()", | ||
"error InvalidInitializer()", | ||
"error InvalidImplementation()", | ||
"error InvalidDeploymentArgs()", | ||
"error InvalidCreateHook()", | ||
"error CreateHookFailed()", | ||
"error CreateHookReverted(string reason)", | ||
"error ImplementationAlreadyExists()" | ||
] |
5 changes: 5 additions & 0 deletions
5
packages/thirdweb/scripts/generate/abis/assets/AssetInfraDeployer.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[ | ||
"function deployInfraProxyDeterministic(address implementation, bytes data, bytes32 salt, bytes extraData) public returns (address deployedProxy)", | ||
"event AssetInfraDeployed(address indexed implementation, address indexed proxy, bytes32 inputSalt, bytes data, bytes extraData)", | ||
"error ProxyDeploymentFailed()" | ||
] |
3 changes: 3 additions & 0 deletions
3
packages/thirdweb/scripts/generate/abis/assets/ERC20Asset.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[ | ||
"function initialize(string _name, string _symbol, string _contractURI, uint256 _maxSupply, address _owner) external" | ||
] |
3 changes: 3 additions & 0 deletions
3
packages/thirdweb/scripts/generate/abis/assets/FeeManager.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[ | ||
"function initialize(address _owner, address _feeRecipient, uint96 _defaultFee) external" | ||
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[ | ||
"function initialize(address _owner) external" | ||
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,331 @@ | ||
import { encodePacked } from "viem"; | ||
import { ZERO_ADDRESS } from "../constants/addresses.js"; | ||
import { getContract } from "../contract/contract.js"; | ||
import { getOrDeployInfraContract } from "../contract/deployment/utils/bootstrap.js"; | ||
import { | ||
deployCreate2Factory, | ||
getDeployedCreate2Factory, | ||
} from "../contract/deployment/utils/create-2-factory.js"; | ||
import { getDeployedInfraContract } from "../contract/deployment/utils/infra.js"; | ||
import { parseEventLogs } from "../event/actions/parse-logs.js"; | ||
import { assetInfraDeployedEvent } from "../extensions/assets/__generated__/AssetInfraDeployer/events/AssetInfraDeployed.js"; | ||
import { deployInfraProxyDeterministic } from "../extensions/assets/__generated__/AssetInfraDeployer/write/deployInfraProxyDeterministic.js"; | ||
import { encodeInitialize as encodeFeeManagerInit } from "../extensions/assets/__generated__/FeeManager/write/initialize.js"; | ||
import { encodeInitialize as encodeRouterInit } from "../extensions/assets/__generated__/Router/write/initialize.js"; | ||
import { sendAndConfirmTransaction } from "../transaction/actions/send-and-confirm-transaction.js"; | ||
import { keccakId } from "../utils/any-evm/keccak-id.js"; | ||
import { isContractDeployed } from "../utils/bytecode/is-contract-deployed.js"; | ||
import { keccak256 } from "../utils/hashing/keccak256.js"; | ||
import type { | ||
ClientAndChain, | ||
ClientAndChainAndAccount, | ||
} from "../utils/types.js"; | ||
import { | ||
DEFAULT_FEE_BPS, | ||
DEFAULT_FEE_RECIPIENT, | ||
DEFAULT_INFRA_ADMIN, | ||
DEFAULT_SALT, | ||
IMPLEMENTATIONS, | ||
} from "./constants.js"; | ||
import { deployInfraProxy } from "./deploy-infra-proxy.js"; | ||
import { getInitCodeHashERC1967 } from "./get-initcode-hash-1967.js"; | ||
|
||
export async function deployRouter(options: ClientAndChainAndAccount) { | ||
let [feeManager, marketSaleImpl] = await Promise.all([ | ||
getDeployedFeeManager(options), | ||
getDeployedInfraContract({ | ||
...options, | ||
contractId: "MarketSale", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}), | ||
]); | ||
|
||
if (!feeManager) { | ||
feeManager = await deployFeeManager(options); | ||
} | ||
|
||
if (!marketSaleImpl) { | ||
marketSaleImpl = await getOrDeployInfraContract({ | ||
...options, | ||
contractId: "MarketSale", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
} | ||
|
||
const assetFactory = await getDeployedAssetFactory(options); | ||
if (!assetFactory) { | ||
throw new Error(`Asset factory not found for chain: ${options.chain.id}`); | ||
} | ||
|
||
const routerImpl = await getOrDeployInfraContract({ | ||
...options, | ||
constructorParams: { | ||
_feeManager: feeManager.address, | ||
_marketSaleImplementation: marketSaleImpl.address, | ||
}, | ||
contractId: "Router", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
|
||
// encode init data | ||
const initData = encodeRouterInit({ | ||
owner: DEFAULT_INFRA_ADMIN, | ||
}); | ||
|
||
const routerProxyAddress = await deployInfraProxy({ | ||
...options, | ||
assetFactory, | ||
extraData: "0x", | ||
implementationAddress: routerImpl.address, | ||
initData, | ||
}); | ||
|
||
return getContract({ | ||
address: routerProxyAddress, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
} | ||
|
||
export async function deployRewardLocker(options: ClientAndChainAndAccount) { | ||
let v3PositionManager = ZERO_ADDRESS; | ||
let v4PositionManager = ZERO_ADDRESS; | ||
|
||
const implementations = IMPLEMENTATIONS[options.chain.id]; | ||
|
||
if (implementations) { | ||
v3PositionManager = implementations.V3PositionManager || ZERO_ADDRESS; | ||
v4PositionManager = implementations.V4PositionManager || ZERO_ADDRESS; | ||
} | ||
|
||
let feeManager = await getDeployedFeeManager(options); | ||
|
||
if (!feeManager) { | ||
feeManager = await deployFeeManager(options); | ||
} | ||
|
||
return await getOrDeployInfraContract({ | ||
...options, | ||
constructorParams: { | ||
_feeManager: feeManager.address, | ||
_v3PositionManager: v3PositionManager, | ||
_v4PositionManager: v4PositionManager, | ||
}, | ||
contractId: "RewardLocker", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
} | ||
|
||
export async function deployFeeManager(options: ClientAndChainAndAccount) { | ||
// asset factory | ||
let assetFactory = await getDeployedAssetFactory(options); | ||
if (!assetFactory) { | ||
assetFactory = await deployAssetFactory(options); | ||
} | ||
|
||
// fee manager implementation | ||
const feeManagerImpl = await getOrDeployInfraContract({ | ||
...options, | ||
contractId: "FeeManager", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
|
||
// encode init data | ||
const initData = encodeFeeManagerInit({ | ||
defaultFee: DEFAULT_FEE_BPS, | ||
feeRecipient: DEFAULT_FEE_RECIPIENT, | ||
owner: DEFAULT_INFRA_ADMIN, | ||
}); | ||
|
||
// fee manager proxy deployment | ||
const transaction = deployInfraProxyDeterministic({ | ||
contract: assetFactory, | ||
data: initData, | ||
extraData: "0x", | ||
implementation: feeManagerImpl.address, | ||
salt: keccakId(DEFAULT_SALT), | ||
}); | ||
|
||
const receipt = await sendAndConfirmTransaction({ | ||
account: options.account, | ||
transaction, | ||
}); | ||
const proxyEvent = assetInfraDeployedEvent(); | ||
const decodedEvent = parseEventLogs({ | ||
events: [proxyEvent], | ||
logs: receipt.logs, | ||
}); | ||
|
||
if (decodedEvent.length === 0 || !decodedEvent[0]) { | ||
throw new Error( | ||
`No AssetInfraDeployed event found in transaction: ${receipt.transactionHash}`, | ||
); | ||
} | ||
|
||
const feeManagerProxyAddress = decodedEvent[0]?.args.proxy; | ||
|
||
return getContract({ | ||
address: feeManagerProxyAddress, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
} | ||
|
||
export async function deployAssetFactory(options: ClientAndChainAndAccount) { | ||
// create2 factory | ||
const create2Factory = await getDeployedCreate2Factory(options); | ||
if (!create2Factory) { | ||
await deployCreate2Factory(options); | ||
} | ||
|
||
// asset factory | ||
return getOrDeployInfraContract({ | ||
...options, | ||
contractId: "AssetInfraDeployer", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
} | ||
|
||
export async function getDeployedRouter(options: ClientAndChain) { | ||
const [feeManager, marketSaleImpl, assetFactory] = await Promise.all([ | ||
getDeployedFeeManager(options), | ||
getDeployedInfraContract({ | ||
...options, | ||
contractId: "MarketSale", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}), | ||
getDeployedAssetFactory(options), | ||
]); | ||
|
||
if (!feeManager || !marketSaleImpl || !assetFactory) { | ||
return null; | ||
} | ||
|
||
const routerImpl = await getDeployedInfraContract({ | ||
...options, | ||
constructorParams: { | ||
_feeManager: feeManager.address, | ||
_marketSaleImplementation: marketSaleImpl.address, | ||
}, | ||
contractId: "Router", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
|
||
if (!routerImpl) { | ||
return null; | ||
} | ||
|
||
const initCodeHash = getInitCodeHashERC1967(routerImpl.address); | ||
|
||
const saltHash = keccak256( | ||
encodePacked( | ||
["bytes32", "address"], | ||
[keccakId(DEFAULT_SALT), DEFAULT_INFRA_ADMIN], | ||
), | ||
); | ||
|
||
const hashedDeployInfo = keccak256( | ||
encodePacked( | ||
["bytes1", "address", "bytes32", "bytes32"], | ||
["0xff", assetFactory.address, saltHash, initCodeHash], | ||
), | ||
); | ||
|
||
const routerProxyAddress = `0x${hashedDeployInfo.slice(26)}`; | ||
const routerProxy = getContract({ | ||
address: routerProxyAddress, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
|
||
if (!(await isContractDeployed(routerProxy))) { | ||
return null; | ||
} | ||
|
||
return routerProxy; | ||
} | ||
|
||
export async function getDeployedRewardLocker(options: ClientAndChain) { | ||
let v3PositionManager = ZERO_ADDRESS; | ||
let v4PositionManager = ZERO_ADDRESS; | ||
|
||
const implementations = IMPLEMENTATIONS[options.chain.id]; | ||
|
||
if (implementations) { | ||
v3PositionManager = implementations.V3PositionManager || ZERO_ADDRESS; | ||
v4PositionManager = implementations.V4PositionManager || ZERO_ADDRESS; | ||
} | ||
|
||
const feeManager = await getDeployedFeeManager(options); | ||
|
||
if (!feeManager) { | ||
return null; | ||
} | ||
|
||
return await getDeployedInfraContract({ | ||
...options, | ||
constructorParams: { | ||
_feeManager: feeManager.address, | ||
_v3PositionManager: v3PositionManager, | ||
_v4PositionManager: v4PositionManager, | ||
}, | ||
contractId: "RewardLocker", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
} | ||
|
||
export async function getDeployedFeeManager(options: ClientAndChain) { | ||
const [assetFactory, feeManagerImpl] = await Promise.all([ | ||
getDeployedAssetFactory(options), | ||
getDeployedInfraContract({ | ||
...options, | ||
contractId: "FeeManager", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}), | ||
]); | ||
|
||
if (!assetFactory || !feeManagerImpl) { | ||
return null; | ||
} | ||
|
||
const initCodeHash = getInitCodeHashERC1967(feeManagerImpl.address); | ||
|
||
const saltHash = keccak256( | ||
encodePacked( | ||
["bytes32", "address"], | ||
[keccakId(DEFAULT_SALT), DEFAULT_INFRA_ADMIN], | ||
), | ||
); | ||
|
||
const hashedDeployInfo = keccak256( | ||
encodePacked( | ||
["bytes1", "address", "bytes32", "bytes32"], | ||
["0xff", assetFactory.address, saltHash, initCodeHash], | ||
), | ||
); | ||
|
||
const feeManagerProxyAddress = `0x${hashedDeployInfo.slice(26)}`; | ||
const feeManagerProxy = getContract({ | ||
address: feeManagerProxyAddress, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
|
||
if (!(await isContractDeployed(feeManagerProxy))) { | ||
return null; | ||
} | ||
|
||
return feeManagerProxy; | ||
} | ||
|
||
export async function getDeployedAssetFactory(args: ClientAndChain) { | ||
const assetFactory = await getDeployedInfraContract({ | ||
...args, | ||
contractId: "AssetInfraDeployer", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
if (!assetFactory) { | ||
return null; | ||
} | ||
return assetFactory; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { ANVIL_CHAIN } from "../../test/src/chains.js"; | ||
import { TEST_CLIENT } from "../../test/src/test-clients.js"; | ||
import { TEST_ACCOUNT_A } from "../../test/src/test-wallets.js"; | ||
import { deployFeeManager, getDeployedFeeManager } from "./bootstrap.js"; | ||
|
||
describe.runIf(process.env.TW_SECRET_KEY)("bootstrap asset infra", () => { | ||
it("should bootstrap fee manager", async () => { | ||
const feeManager = await deployFeeManager({ | ||
account: TEST_ACCOUNT_A, | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
}); | ||
|
||
const expectedFeeManager = await getDeployedFeeManager({ | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
}); | ||
|
||
expect(expectedFeeManager).toBeDefined(); | ||
expect(feeManager.address.toLowerCase()).to.equal( | ||
expectedFeeManager?.address?.toLowerCase(), | ||
); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
export const DEFAULT_MAX_SUPPLY_ERC20 = 10_000_000_000n; | ||
export const DEFAULT_POOL_FEE = 10000; | ||
export const DEFAULT_POOL_INITIAL_TICK = 230200; | ||
export const DEFAULT_INFRA_ADMIN = "0x1a472863cf21d5aa27f417df9140400324c48f22"; | ||
export const DEFAULT_FEE_RECIPIENT = | ||
"0x1af20c6b23373350ad464700b5965ce4b0d2ad94"; | ||
export const DEFAULT_FEE_BPS = 50n; | ||
export const DEFAULT_SALT = "thirdweb"; | ||
|
||
export const IMPLEMENTATIONS: Record<number, Record<string, string>> = { | ||
8453: { | ||
AssetEntrypointERC20: "0x7FF679bFb89ee0F88645CAb8Ab0844ea485a3434", | ||
ERC20AssetImpl: "", | ||
V3PositionManager: "", | ||
V4PositionManager: "", | ||
}, | ||
84532: { | ||
AssetEntrypointERC20: "0x79C1236cFe59f1f088A15Da08b0D8667387d9703", | ||
ERC20AssetImpl: "", | ||
V3PositionManager: "", | ||
V4PositionManager: "", | ||
}, | ||
}; | ||
|
||
export enum ImplementationType { | ||
CLONE = 0, | ||
CLONE_WITH_IMMUTABLE_ARGS = 1, | ||
ERC1967 = 2, | ||
ERC1967_WITH_IMMUTABLE_ARGS = 3, | ||
} | ||
|
||
export enum CreateHook { | ||
NONE = 0, // do nothing | ||
CREATE_POOL = 1, // create a DEX pool via Router | ||
CREATE_MARKET = 2, // create a market sale via Router | ||
DISTRIBUTE = 3, // distribute tokens to recipients | ||
EXTERNAL_HOOK = 4, // call an external hook contract | ||
} |
130 changes: 130 additions & 0 deletions
130
packages/thirdweb/src/assets/create-token-by-impl-config.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import type { Hex } from "viem"; | ||
import { NATIVE_TOKEN_ADDRESS, ZERO_ADDRESS } from "../constants/addresses.js"; | ||
import { getContract } from "../contract/contract.js"; | ||
import { parseEventLogs } from "../event/actions/parse-logs.js"; | ||
import { assetCreatedEvent } from "../extensions/assets/__generated__/AssetEntrypointERC20/events/AssetCreated.js"; | ||
import { createAssetByImplementationConfig } from "../extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetByImplementationConfig.js"; | ||
import { decimals } from "../extensions/erc20/read/decimals.js"; | ||
import { eth_blockNumber } from "../rpc/actions/eth_blockNumber.js"; | ||
import { getRpcClient } from "../rpc/rpc.js"; | ||
import { sendAndConfirmTransaction } from "../transaction/actions/send-and-confirm-transaction.js"; | ||
import { keccakId } from "../utils/any-evm/keccak-id.js"; | ||
import { toHex } from "../utils/encoding/hex.js"; | ||
import { toUnits } from "../utils/units.js"; | ||
import { | ||
CreateHook, | ||
DEFAULT_MAX_SUPPLY_ERC20, | ||
ImplementationType, | ||
} from "./constants.js"; | ||
import { getOrDeployEntrypointERC20 } from "./get-entrypoint-erc20.js"; | ||
import { getOrDeployERC20AssetImpl } from "./get-erc20-asset-impl.js"; | ||
import { | ||
encodeInitParams, | ||
encodeMarketConfig, | ||
encodePoolConfig, | ||
} from "./token-utils.js"; | ||
import type { CreateTokenOptions } from "./types.js"; | ||
|
||
export async function createTokenByImplConfig(options: CreateTokenOptions) { | ||
const { client, chain, account, params, launchConfig } = options; | ||
|
||
const creator = params.owner || account.address; | ||
|
||
const encodedInitData = await encodeInitParams({ | ||
client, | ||
creator, | ||
params, | ||
}); | ||
|
||
const rpcRequest = getRpcClient({ | ||
...options, | ||
}); | ||
const blockNumber = await eth_blockNumber(rpcRequest); | ||
const salt = options.salt | ||
? options.salt.startsWith("0x") && options.salt.length === 66 | ||
? (options.salt as `0x${string}`) | ||
: keccakId(options.salt) | ||
: toHex(blockNumber, { | ||
size: 32, | ||
}); | ||
|
||
const entrypoint = await getOrDeployEntrypointERC20(options); | ||
const tokenImpl = await getOrDeployERC20AssetImpl(options); | ||
|
||
let hookData: Hex = "0x"; | ||
let amount = toUnits( | ||
params.maxSupply.toString() || DEFAULT_MAX_SUPPLY_ERC20.toString(), | ||
18, | ||
); | ||
if (launchConfig?.kind === "pool") { | ||
hookData = encodePoolConfig(launchConfig.config); | ||
amount = toUnits( | ||
launchConfig.config.amount.toString() || | ||
DEFAULT_MAX_SUPPLY_ERC20.toString(), | ||
18, | ||
); | ||
} else if (launchConfig?.kind === "market") { | ||
const currencyContract = | ||
launchConfig.config.tokenOut && | ||
launchConfig.config.tokenOut !== NATIVE_TOKEN_ADDRESS | ||
? getContract({ | ||
address: launchConfig.config.tokenOut, | ||
chain, | ||
client, | ||
}) | ||
: null; | ||
const currencyDecimals = launchConfig.config.priceDenominator | ||
? launchConfig.config.priceDenominator | ||
: currencyContract | ||
? await decimals({ | ||
contract: currencyContract, | ||
}) | ||
: 18; | ||
|
||
hookData = encodeMarketConfig({ | ||
...launchConfig.config, | ||
decimals: currencyDecimals, | ||
}); | ||
} | ||
|
||
const transaction = createAssetByImplementationConfig({ | ||
config: { | ||
contractId: keccakId("ERC20Asset"), | ||
createHook: | ||
launchConfig?.kind === "pool" | ||
? CreateHook.CREATE_POOL | ||
: launchConfig?.kind === "market" | ||
? CreateHook.CREATE_MARKET | ||
: launchConfig?.kind === "distribute" | ||
? CreateHook.DISTRIBUTE | ||
: CreateHook.NONE, | ||
createHookData: hookData, | ||
implementation: tokenImpl.address, | ||
implementationType: ImplementationType.ERC1967, | ||
}, | ||
contract: entrypoint, | ||
creator, | ||
params: { | ||
amount, | ||
data: encodedInitData, | ||
hookData, | ||
referrer: ZERO_ADDRESS, | ||
salt, | ||
}, | ||
}); | ||
|
||
const receipt = await sendAndConfirmTransaction({ account, transaction }); | ||
const assetEvent = assetCreatedEvent(); | ||
const decodedEvent = parseEventLogs({ | ||
events: [assetEvent], | ||
logs: receipt.logs, | ||
}); | ||
|
||
if (decodedEvent.length === 0 || !decodedEvent[0]) { | ||
throw new Error( | ||
`No AssetCreated event found in transaction: ${receipt.transactionHash}`, | ||
); | ||
} | ||
|
||
return decodedEvent[0]?.args.asset; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { ANVIL_CHAIN } from "../../test/src/chains.js"; | ||
import { TEST_CLIENT } from "../../test/src/test-clients.js"; | ||
import { TEST_ACCOUNT_A } from "../../test/src/test-wallets.js"; | ||
import { getContract } from "../contract/contract.js"; | ||
import { name } from "../extensions/common/read/name.js"; | ||
// import { totalSupply } from "../extensions/erc20/__generated__/IERC20/read/totalSupply.js"; | ||
import { createTokenByImplConfig } from "./create-token-by-impl-config.js"; | ||
|
||
describe.runIf(process.env.TW_SECRET_KEY)("create token by impl config", () => { | ||
it("should create token without pool", async () => { | ||
const token = await createTokenByImplConfig({ | ||
account: TEST_ACCOUNT_A, | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
params: { | ||
maxSupply: 10_00n, | ||
name: "Test", | ||
}, | ||
salt: "salt123", | ||
}); | ||
|
||
expect(token).toBeDefined(); | ||
|
||
const tokenName = await name({ | ||
contract: getContract({ | ||
address: token, | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
}), | ||
}); | ||
expect(tokenName).to.eq("Test"); | ||
|
||
// const supply = await totalSupply({ | ||
// contract: getContract({ | ||
// client: TEST_CLIENT, | ||
// chain: ANVIL_CHAIN, | ||
// address: token, | ||
// }), | ||
// }); | ||
|
||
// console.log("supply: ", supply); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import type { Hex } from "viem"; | ||
import { NATIVE_TOKEN_ADDRESS, ZERO_ADDRESS } from "../constants/addresses.js"; | ||
import { getContract } from "../contract/contract.js"; | ||
import { parseEventLogs } from "../event/actions/parse-logs.js"; | ||
import { assetCreatedEvent } from "../extensions/assets/__generated__/AssetEntrypointERC20/events/AssetCreated.js"; | ||
import { createAsset } from "../extensions/assets/__generated__/AssetEntrypointERC20/write/createAsset.js"; | ||
import { decimals } from "../extensions/erc20/read/decimals.js"; | ||
import { eth_blockNumber } from "../rpc/actions/eth_blockNumber.js"; | ||
import { getRpcClient } from "../rpc/rpc.js"; | ||
import { sendAndConfirmTransaction } from "../transaction/actions/send-and-confirm-transaction.js"; | ||
import { keccakId } from "../utils/any-evm/keccak-id.js"; | ||
import { toHex } from "../utils/encoding/hex.js"; | ||
import { toUnits } from "../utils/units.js"; | ||
import { DEFAULT_MAX_SUPPLY_ERC20 } from "./constants.js"; | ||
import { getOrDeployEntrypointERC20 } from "./get-entrypoint-erc20.js"; | ||
import { | ||
encodeInitParams, | ||
encodeMarketConfig, | ||
encodePoolConfig, | ||
} from "./token-utils.js"; | ||
import type { CreateTokenOptions } from "./types.js"; | ||
|
||
export async function createToken(options: CreateTokenOptions) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remember to generate the TSdoc for this with multiple examples |
||
const { client, chain, account, params, launchConfig } = options; | ||
|
||
const creator = params.owner || account.address; | ||
|
||
const encodedInitData = await encodeInitParams({ | ||
client, | ||
creator, | ||
params, | ||
}); | ||
|
||
const rpcRequest = getRpcClient({ | ||
...options, | ||
}); | ||
const blockNumber = await eth_blockNumber(rpcRequest); | ||
const salt = options.salt | ||
? options.salt.startsWith("0x") && options.salt.length === 66 | ||
? (options.salt as `0x${string}`) | ||
: keccakId(options.salt) | ||
: toHex(blockNumber, { | ||
size: 32, | ||
}); | ||
|
||
const entrypoint = await getOrDeployEntrypointERC20(options); | ||
|
||
let hookData: Hex = "0x"; | ||
let amount = toUnits( | ||
params.maxSupply.toString() || DEFAULT_MAX_SUPPLY_ERC20.toString(), | ||
18, | ||
); | ||
if (launchConfig?.kind === "pool") { | ||
hookData = encodePoolConfig(launchConfig.config); | ||
amount = toUnits( | ||
launchConfig.config.amount.toString() || | ||
DEFAULT_MAX_SUPPLY_ERC20.toString(), | ||
18, | ||
); | ||
} else if (launchConfig?.kind === "market") { | ||
const currencyContract = | ||
launchConfig.config.tokenOut && | ||
launchConfig.config.tokenOut !== NATIVE_TOKEN_ADDRESS | ||
? getContract({ | ||
address: launchConfig.config.tokenOut, | ||
chain, | ||
client, | ||
}) | ||
: null; | ||
const currencyDecimals = launchConfig.config.priceDenominator | ||
? launchConfig.config.priceDenominator | ||
: currencyContract | ||
? await decimals({ | ||
contract: currencyContract, | ||
}) | ||
: 18; | ||
|
||
hookData = encodeMarketConfig({ | ||
...launchConfig.config, | ||
decimals: currencyDecimals, | ||
}); | ||
} | ||
|
||
const transaction = createAsset({ | ||
contract: entrypoint, | ||
createParams: { | ||
amount, | ||
data: encodedInitData, | ||
hookData, | ||
referrer: ZERO_ADDRESS, | ||
salt, | ||
}, | ||
creator, | ||
}); | ||
|
||
const receipt = await sendAndConfirmTransaction({ account, transaction }); | ||
const assetEvent = assetCreatedEvent(); | ||
const decodedEvent = parseEventLogs({ | ||
events: [assetEvent], | ||
logs: receipt.logs, | ||
}); | ||
|
||
if (decodedEvent.length === 0 || !decodedEvent[0]) { | ||
throw new Error( | ||
`No AssetCreated event found in transaction: ${receipt.transactionHash}`, | ||
); | ||
} | ||
|
||
return decodedEvent[0]?.args.asset; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import type { Hex } from "viem"; | ||
import type { ThirdwebContract } from "../contract/contract.js"; | ||
import { parseEventLogs } from "../event/actions/parse-logs.js"; | ||
import { assetInfraDeployedEvent } from "../extensions/assets/__generated__/AssetInfraDeployer/events/AssetInfraDeployed.js"; | ||
import { deployInfraProxyDeterministic } from "../extensions/assets/__generated__/AssetInfraDeployer/write/deployInfraProxyDeterministic.js"; | ||
import { sendAndConfirmTransaction } from "../transaction/actions/send-and-confirm-transaction.js"; | ||
import { keccakId } from "../utils/any-evm/keccak-id.js"; | ||
import type { ClientAndChainAndAccount } from "../utils/types.js"; | ||
import { DEFAULT_SALT } from "./constants.js"; | ||
|
||
export async function deployInfraProxy( | ||
options: ClientAndChainAndAccount & { | ||
assetFactory: ThirdwebContract; | ||
implementationAddress: string; | ||
initData: Hex; | ||
extraData: Hex; | ||
}, | ||
) { | ||
const transaction = deployInfraProxyDeterministic({ | ||
contract: options.assetFactory, | ||
data: options.initData, | ||
extraData: options.extraData, | ||
implementation: options.implementationAddress, | ||
salt: keccakId(DEFAULT_SALT), | ||
}); | ||
|
||
const receipt = await sendAndConfirmTransaction({ | ||
account: options.account, | ||
transaction, | ||
}); | ||
const proxyEvent = assetInfraDeployedEvent(); | ||
const decodedEvent = parseEventLogs({ | ||
events: [proxyEvent], | ||
logs: receipt.logs, | ||
}); | ||
|
||
if (decodedEvent.length === 0 || !decodedEvent[0]) { | ||
throw new Error( | ||
`No AssetInfraDeployed event found in transaction: ${receipt.transactionHash}`, | ||
); | ||
} | ||
|
||
return decodedEvent[0]?.args.proxy; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { getContract, type ThirdwebContract } from "src/contract/contract.js"; | ||
import { getBalance } from "src/extensions/erc20/read/getBalance.js"; | ||
import { approve } from "src/extensions/erc20/write/approve.js"; | ||
import { sendAndConfirmTransaction } from "src/transaction/actions/send-and-confirm-transaction.js"; | ||
import { toUnits } from "src/utils/units.js"; | ||
import { beforeAll, describe, expect, it } from "vitest"; | ||
import { ANVIL_CHAIN } from "../../test/src/chains.js"; | ||
import { TEST_CLIENT } from "../../test/src/test-clients.js"; | ||
import { | ||
TEST_ACCOUNT_A, | ||
TEST_ACCOUNT_B, | ||
TEST_ACCOUNT_C, | ||
TEST_ACCOUNT_D, | ||
} from "../../test/src/test-wallets.js"; | ||
import { createTokenByImplConfig } from "./create-token-by-impl-config.js"; | ||
import { distributeToken } from "./distribute-token.js"; | ||
import { getDeployedEntrypointERC20 } from "./get-entrypoint-erc20.js"; | ||
|
||
describe.runIf(process.env.TW_SECRET_KEY)( | ||
"create token by impl config", | ||
{ | ||
timeout: 20000, | ||
}, | ||
() => { | ||
let token: ThirdwebContract; | ||
beforeAll(async () => { | ||
// create token | ||
const tokenAddress = await createTokenByImplConfig({ | ||
account: TEST_ACCOUNT_A, | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
params: { | ||
maxSupply: 10_000_000_000n, | ||
name: "Test", | ||
}, | ||
salt: "salt123", | ||
}); | ||
|
||
token = getContract({ | ||
address: tokenAddress, | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
}); | ||
|
||
// approve tokens to entrypoint for distribution | ||
const entrypoint = await getDeployedEntrypointERC20({ | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
}); | ||
|
||
const approvalTx = approve({ | ||
amountWei: toUnits("1000", 18), | ||
contract: token, | ||
spender: entrypoint?.address as string, | ||
}); | ||
await sendAndConfirmTransaction({ | ||
account: TEST_ACCOUNT_A, | ||
transaction: approvalTx, | ||
}); | ||
}, 20000); | ||
|
||
it("should distrbute tokens to specified recipients", async () => { | ||
const contents = [ | ||
{ amount: 10n, recipient: TEST_ACCOUNT_B.address }, | ||
{ amount: 15n, recipient: TEST_ACCOUNT_C.address }, | ||
{ amount: 20n, recipient: TEST_ACCOUNT_D.address }, | ||
]; | ||
|
||
const transaction = await distributeToken({ | ||
chain: ANVIL_CHAIN, | ||
client: TEST_CLIENT, | ||
contents, | ||
tokenAddress: token.address, | ||
}); | ||
|
||
await sendAndConfirmTransaction({ account: TEST_ACCOUNT_A, transaction }); | ||
|
||
const balanceB = ( | ||
await getBalance({ | ||
address: TEST_ACCOUNT_B.address, | ||
contract: token, | ||
}) | ||
).value; | ||
|
||
const balanceC = ( | ||
await getBalance({ | ||
address: TEST_ACCOUNT_C.address, | ||
contract: token, | ||
}) | ||
).value; | ||
|
||
const balanceD = ( | ||
await getBalance({ | ||
address: TEST_ACCOUNT_D.address, | ||
contract: token, | ||
}) | ||
).value; | ||
|
||
// admin balance | ||
const balanceA = ( | ||
await getBalance({ | ||
address: TEST_ACCOUNT_A.address, | ||
contract: token, | ||
}) | ||
).value; | ||
|
||
expect(balanceB).to.equal(toUnits("10", 18)); | ||
expect(balanceC).to.equal(toUnits("15", 18)); | ||
expect(balanceD).to.equal(toUnits("20", 18)); | ||
|
||
expect(balanceA).to.equal( | ||
toUnits("10000000000", 18) - balanceB - balanceC - balanceD, | ||
); | ||
}); | ||
}, | ||
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { distributeAsset } from "../extensions/assets/__generated__/AssetEntrypointERC20/write/distributeAsset.js"; | ||
import type { ClientAndChain } from "../utils/types.js"; | ||
import { toUnits } from "../utils/units.js"; | ||
import { getDeployedEntrypointERC20 } from "./get-entrypoint-erc20.js"; | ||
import type { DistributeContent } from "./types.js"; | ||
|
||
type DistrbuteTokenParams = ClientAndChain & { | ||
tokenAddress: string; | ||
contents: DistributeContent[]; | ||
}; | ||
|
||
export async function distributeToken(options: DistrbuteTokenParams) { | ||
const entrypoint = await getDeployedEntrypointERC20(options); | ||
|
||
if (!entrypoint) { | ||
throw new Error(`Entrypoint not found on chain: ${options.chain.id}`); | ||
} | ||
|
||
return distributeAsset({ | ||
asset: options.tokenAddress, | ||
contents: options.contents.map((a) => { | ||
return { ...a, amount: toUnits(a.amount.toString(), 18) }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is the decimals always 18 here? |
||
}), | ||
contract: entrypoint, | ||
}); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { encodePacked } from "viem"; | ||
import { ZERO_ADDRESS } from "../constants/addresses.js"; | ||
import { getContract, type ThirdwebContract } from "../contract/contract.js"; | ||
import { getOrDeployInfraContract } from "../contract/deployment/utils/bootstrap.js"; | ||
import { getDeployedInfraContract } from "../contract/deployment/utils/infra.js"; | ||
import { encodeInitialize } from "../extensions/assets/__generated__/AssetEntrypointERC20/write/initialize.js"; | ||
import { keccakId } from "../utils/any-evm/keccak-id.js"; | ||
import { isContractDeployed } from "../utils/bytecode/is-contract-deployed.js"; | ||
import { keccak256 } from "../utils/hashing/keccak256.js"; | ||
import type { | ||
ClientAndChain, | ||
ClientAndChainAndAccount, | ||
} from "../utils/types.js"; | ||
import { deployAssetFactory, getDeployedAssetFactory } from "./bootstrap.js"; | ||
import { | ||
DEFAULT_INFRA_ADMIN, | ||
DEFAULT_SALT, | ||
IMPLEMENTATIONS, | ||
} from "./constants.js"; | ||
import { deployInfraProxy } from "./deploy-infra-proxy.js"; | ||
import { getInitCodeHashERC1967 } from "./get-initcode-hash-1967.js"; | ||
|
||
export async function getOrDeployEntrypointERC20( | ||
options: ClientAndChainAndAccount, | ||
): Promise<ThirdwebContract> { | ||
const implementations = IMPLEMENTATIONS[options.chain.id]; | ||
|
||
if (implementations?.AssetEntrypointERC20) { | ||
return getContract({ | ||
address: implementations.AssetEntrypointERC20, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
} | ||
|
||
let assetFactory = await getDeployedAssetFactory(options); | ||
if (!assetFactory) { | ||
assetFactory = await deployAssetFactory(options); | ||
} | ||
|
||
const entrypointImpl = await getOrDeployInfraContract({ | ||
...options, | ||
contractId: "AssetEntrypointERC20", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
version: "0.0.2", | ||
}); | ||
|
||
// encode init data | ||
const initData = encodeInitialize({ | ||
owner: DEFAULT_INFRA_ADMIN, | ||
rewardLocker: ZERO_ADDRESS, | ||
router: ZERO_ADDRESS, | ||
}); | ||
|
||
const entyrpointProxyAddress = await deployInfraProxy({ | ||
...options, | ||
assetFactory, | ||
extraData: "0x", | ||
implementationAddress: entrypointImpl.address, | ||
initData, | ||
}); | ||
|
||
return getContract({ | ||
address: entyrpointProxyAddress, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
} | ||
|
||
export async function getDeployedEntrypointERC20(options: ClientAndChain) { | ||
const implementations = IMPLEMENTATIONS[options.chain.id]; | ||
|
||
if (implementations?.AssetEntrypointERC20) { | ||
return getContract({ | ||
address: implementations.AssetEntrypointERC20, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
} | ||
|
||
const [assetFactory, entrypointImpl] = await Promise.all([ | ||
getDeployedAssetFactory(options), | ||
getDeployedInfraContract({ | ||
...options, | ||
contractId: "AssetEntrypointERC20", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
version: "0.0.2", | ||
}), | ||
]); | ||
|
||
if (!assetFactory || !entrypointImpl) { | ||
return null; | ||
} | ||
|
||
const initCodeHash = getInitCodeHashERC1967(entrypointImpl.address); | ||
|
||
const saltHash = keccak256( | ||
encodePacked( | ||
["bytes32", "address"], | ||
[keccakId(DEFAULT_SALT), DEFAULT_INFRA_ADMIN], | ||
), | ||
); | ||
|
||
const hashedDeployInfo = keccak256( | ||
encodePacked( | ||
["bytes1", "address", "bytes32", "bytes32"], | ||
["0xff", assetFactory.address, saltHash, initCodeHash], | ||
), | ||
); | ||
|
||
const entrypointProxyAddress = `0x${hashedDeployInfo.slice(26)}`; | ||
const entrypointProxy = getContract({ | ||
address: entrypointProxyAddress, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
|
||
if (!(await isContractDeployed(entrypointProxy))) { | ||
return null; | ||
} | ||
|
||
return entrypointProxy; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { getContract, type ThirdwebContract } from "../contract/contract.js"; | ||
import { getOrDeployInfraContract } from "../contract/deployment/utils/bootstrap.js"; | ||
import type { ClientAndChainAndAccount } from "../utils/types.js"; | ||
import { IMPLEMENTATIONS } from "./constants.js"; | ||
|
||
export async function getOrDeployERC20AssetImpl( | ||
options: ClientAndChainAndAccount, | ||
): Promise<ThirdwebContract> { | ||
const implementations = IMPLEMENTATIONS[options.chain.id]; | ||
|
||
if (implementations?.ERC20AssetImpl) { | ||
return getContract({ | ||
address: implementations.ERC20AssetImpl, | ||
chain: options.chain, | ||
client: options.client, | ||
}); | ||
} | ||
|
||
return await getOrDeployInfraContract({ | ||
...options, | ||
contractId: "ERC20Asset", | ||
publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", | ||
}); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { keccak256 } from "../utils/hashing/keccak256.js"; | ||
|
||
export function getInitCodeHashERC1967(implementation: string) { | ||
// See `initCodeHashERC1967` - LibClone {https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol} | ||
return keccak256( | ||
`0x603d3d8160223d3973${implementation.toLowerCase().replace(/^0x/, "")}60095155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3`, | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { ZERO_ADDRESS } from "../constants/addresses.js"; | ||
import { getRouter } from "../extensions/assets/__generated__/AssetEntrypointERC20/read/getRouter.js"; | ||
import type { ClientAndChain } from "../utils/types.js"; | ||
import { getDeployedEntrypointERC20 } from "./get-entrypoint-erc20.js"; | ||
|
||
export async function isRouterEnabled( | ||
options: ClientAndChain, | ||
): Promise<boolean> { | ||
const entrypoint = await getDeployedEntrypointERC20(options); | ||
|
||
if (!entrypoint) { | ||
return false; | ||
} | ||
|
||
const router = await getRouter({ | ||
contract: entrypoint, | ||
}); | ||
|
||
return router !== ZERO_ADDRESS; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import type { Hex } from "viem"; | ||
import type { ThirdwebClient } from "../client/client.js"; | ||
import { NATIVE_TOKEN_ADDRESS, ZERO_ADDRESS } from "../constants/addresses.js"; | ||
import { encodeInitialize } from "../extensions/assets/__generated__/ERC20Asset/write/initialize.js"; | ||
import { upload } from "../storage/upload.js"; | ||
import { encodeAbiParameters } from "../utils/abi/encodeAbiParameters.js"; | ||
import { toUnits } from "../utils/units.js"; | ||
import { | ||
DEFAULT_MAX_SUPPLY_ERC20, | ||
DEFAULT_POOL_FEE, | ||
DEFAULT_POOL_INITIAL_TICK, | ||
} from "./constants.js"; | ||
import type { MarketConfig, PoolConfig, TokenParams } from "./types.js"; | ||
|
||
export async function encodeInitParams(options: { | ||
client: ThirdwebClient; | ||
params: TokenParams; | ||
creator: string; | ||
}): Promise<Hex> { | ||
const { client, params, creator } = options; | ||
|
||
const contractURI = | ||
options.params.contractURI || | ||
(await upload({ | ||
client, | ||
files: [ | ||
{ | ||
description: params.description, | ||
external_link: params.external_link, | ||
image: params.image, | ||
name: params.name, | ||
social_urls: params.social_urls, | ||
symbol: params.symbol, | ||
}, | ||
], | ||
})) || | ||
""; | ||
|
||
return encodeInitialize({ | ||
contractURI, | ||
maxSupply: toUnits( | ||
params.maxSupply.toString() || DEFAULT_MAX_SUPPLY_ERC20.toString(), | ||
18, | ||
), | ||
name: params.name, | ||
owner: creator, | ||
symbol: params.symbol || params.name, | ||
}); | ||
} | ||
|
||
export function encodePoolConfig(poolConfig: PoolConfig): Hex { | ||
const POOL_PARAMS = [ | ||
{ | ||
name: "currency", | ||
type: "address", | ||
}, | ||
{ | ||
name: "amount", | ||
type: "uint256", | ||
}, | ||
{ | ||
name: "fee", | ||
type: "uint24", | ||
}, | ||
{ | ||
name: "initialTick", | ||
type: "uint24", | ||
}, | ||
] as const; | ||
|
||
return encodeAbiParameters(POOL_PARAMS, [ | ||
poolConfig.currency || NATIVE_TOKEN_ADDRESS, | ||
toUnits(poolConfig.amount.toString(), 18), | ||
poolConfig.fee || DEFAULT_POOL_FEE, | ||
poolConfig.initialTick || DEFAULT_POOL_INITIAL_TICK, | ||
]); | ||
} | ||
|
||
export function encodeMarketConfig( | ||
marketConfig: MarketConfig & { decimals: number }, | ||
): Hex { | ||
const MARKET_PARAMS = [ | ||
{ | ||
name: "tokenOut", | ||
type: "address", | ||
}, | ||
{ | ||
name: "pricePerUnit", | ||
type: "uint256", | ||
}, | ||
{ | ||
name: "priceDenominator", | ||
type: "uint8", | ||
}, | ||
{ | ||
name: "startTime", | ||
type: "uint48", | ||
}, | ||
{ | ||
name: "endTime", | ||
type: "uint48", | ||
}, | ||
{ | ||
name: "hook", | ||
type: "address", | ||
}, | ||
{ | ||
name: "hookInit", | ||
type: "bytes", | ||
}, | ||
] as const; | ||
|
||
return encodeAbiParameters(MARKET_PARAMS, [ | ||
marketConfig.tokenOut || NATIVE_TOKEN_ADDRESS, | ||
marketConfig.pricePerUnit, | ||
marketConfig.priceDenominator || marketConfig.decimals || 18, | ||
marketConfig.startTime || 0, | ||
marketConfig.endTime || 0, | ||
ZERO_ADDRESS, | ||
"0x", | ||
]); | ||
} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import type { FileOrBufferOrString } from "../storage/upload/types.js"; | ||
import type { ClientAndChainAndAccount } from "../utils/types.js"; | ||
|
||
export type TokenParams = { | ||
name: string; | ||
description?: string; | ||
image?: FileOrBufferOrString; | ||
external_link?: string; | ||
social_urls?: Record<string, string>; | ||
symbol?: string; | ||
contractURI?: string; | ||
maxSupply: bigint; | ||
owner?: string; | ||
}; | ||
|
||
export type PoolConfig = { | ||
amount: bigint; | ||
currency?: string; | ||
fee?: number; | ||
initialTick?: number; | ||
}; | ||
|
||
export type MarketConfig = { | ||
tokenOut?: string; | ||
pricePerUnit: bigint; | ||
priceDenominator?: number; | ||
startTime?: number; | ||
endTime?: number; | ||
hookAddress?: string; | ||
hookInitData?: string; | ||
}; | ||
|
||
export type DistributeContent = { | ||
amount: bigint; | ||
recipient: string; | ||
}; | ||
|
||
type DistributeConfig = { | ||
content: DistributeContent[]; | ||
}; | ||
|
||
type LaunchConfig = | ||
| { kind: "pool"; config: PoolConfig } | ||
| { kind: "market"; config: MarketConfig } | ||
| { kind: "distribute"; config: DistributeConfig }; | ||
|
||
export type CreateTokenOptions = ClientAndChainAndAccount & { | ||
salt?: string; | ||
params: TokenParams; | ||
launchConfig?: LaunchConfig; | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
export { | ||
deployAssetFactory, | ||
deployFeeManager, | ||
deployRewardLocker, | ||
deployRouter, | ||
getDeployedAssetFactory, | ||
getDeployedFeeManager, | ||
getDeployedRewardLocker, | ||
getDeployedRouter, | ||
} from "../assets/bootstrap.js"; | ||
export { createToken } from "../assets/create-token.js"; | ||
export { createTokenByImplConfig } from "../assets/create-token-by-impl-config.js"; | ||
export { distributeToken } from "../assets/distribute-token.js"; | ||
export { getDeployedEntrypointERC20 } from "../assets/get-entrypoint-erc20.js"; | ||
export { isRouterEnabled } from "../assets/is-router-enabled.js"; | ||
export type { | ||
CreateTokenOptions, | ||
DistributeContent, | ||
MarketConfig, | ||
PoolConfig, | ||
TokenParams, | ||
} from "../assets/types.js"; |
47 changes: 47 additions & 0 deletions
47
.../thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/AssetCreated.ts
Oops, something went wrong.
25 changes: 25 additions & 0 deletions
25
...rdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/AssetDistributed.ts
Oops, something went wrong.
43 changes: 43 additions & 0 deletions
43
...eb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/ImplementationAdded.ts
Oops, something went wrong.
42 changes: 42 additions & 0 deletions
42
...eb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/RewardLockerUpdated.ts
Oops, something went wrong.
40 changes: 40 additions & 0 deletions
40
...thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/RouterUpdated.ts
Oops, something went wrong.
152 changes: 152 additions & 0 deletions
152
...irdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/read/getImplementation.ts
Oops, something went wrong.
70 changes: 70 additions & 0 deletions
70
...thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/read/getRewardLocker.ts
Oops, something went wrong.
70 changes: 70 additions & 0 deletions
70
packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/read/getRouter.ts
Oops, something went wrong.
181 changes: 181 additions & 0 deletions
181
...rdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/addImplementation.ts
Oops, something went wrong.
196 changes: 196 additions & 0 deletions
196
packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/buyAsset.ts
Oops, something went wrong.
187 changes: 187 additions & 0 deletions
187
...es/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAsset.ts
Oops, something went wrong.
203 changes: 203 additions & 0 deletions
203
...hirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetById.ts
Oops, something went wrong.
238 changes: 238 additions & 0 deletions
238
...ions/assets/__generated__/AssetEntrypointERC20/write/createAssetByImplementationConfig.ts
Oops, something went wrong.
164 changes: 164 additions & 0 deletions
164
...hirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/distributeAsset.ts
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would put this in a constant with a big TODO to update it before merge