From fcff2b744b5fa9a00046dc791bb0cc54fb1306b5 Mon Sep 17 00:00:00 2001 From: josemarinas <36479864+josemarinas@users.noreply.github.com> Date: Thu, 17 Aug 2023 11:10:56 +0200 Subject: [PATCH] Feature: Add support for erc1155 deposits and withdrawals (#268) * add support for erc1155 deposits and withdrawals * update changelog * add different tokens to deposit estimation * update use bytecode from oz instead of harcoded one * fix code smell * add comments to decoders * Update modules/client/test/integration/client/decoding.test.ts Co-authored-by: Michael Heuer <20623991+Michael-A-Heuer@users.noreply.github.com> * Update modules/client/test/integration/client/decoding.test.ts Co-authored-by: Michael Heuer <20623991+Michael-A-Heuer@users.noreply.github.com> * add separate native transfer function * fix rebase * fix satsuma version * update subgraph endpoint version --------- Co-authored-by: Michael Heuer <20623991+Michael-A-Heuer@users.noreply.github.com> --- modules/client-common/src/types.ts | 1 + modules/client/CHANGELOG.md | 1 + .../examples/01-client/05-deposit-erc1155.ts | 74 +++++++ .../{05-get-dao.ts => 06-get-dao.ts} | 0 .../{06-get-daos.ts => 07-get-daos.ts} | 0 ...{07-get-balances.ts => 08-get-balances.ts} | 0 ...8-get-transfers.ts => 09-get-transfers.ts} | 0 ...9-set-allowance.ts => 10-set-allowance.ts} | 0 ...{10-pin-metadata.ts => 11-pin-metadata.ts} | 0 ...allation.ts => 12-prepare-installation.ts} | 0 ...-version.ts => 13-get-protocol-version.ts} | 0 ...lation.ts => 14-prepare-uninstallation.ts} | 0 .../05-encoders-decoders/06-withdraw.ts | 46 ++++- .../client/src/internal/client/decoding.ts | 16 ++ .../client/src/internal/client/encoding.ts | 48 +++++ .../client/src/internal/client/estimation.ts | 50 ++--- modules/client/src/internal/client/methods.ts | 186 +++++++++++++----- modules/client/src/internal/utils.ts | 108 +++++++++- modules/client/src/types.ts | 29 ++- modules/client/test/helpers/deploy-erc1155.ts | 25 +++ modules/client/test/helpers/deploy-erc20.ts | 18 +- modules/client/test/helpers/deploy-erc721.ts | 19 +- .../test/integration/client/decoding.test.ts | 40 ++++ .../test/integration/client/encoding.test.ts | 25 +++ .../integration/client/estimation.test.ts | 63 ++++++ .../test/integration/client/methods.test.ts | 135 +++++++++++++ modules/client/test/integration/constants.ts | 2 +- modules/common/src/errors.ts | 8 + 28 files changed, 799 insertions(+), 95 deletions(-) create mode 100644 modules/client/examples/01-client/05-deposit-erc1155.ts rename modules/client/examples/01-client/{05-get-dao.ts => 06-get-dao.ts} (100%) rename modules/client/examples/01-client/{06-get-daos.ts => 07-get-daos.ts} (100%) rename modules/client/examples/01-client/{07-get-balances.ts => 08-get-balances.ts} (100%) rename modules/client/examples/01-client/{08-get-transfers.ts => 09-get-transfers.ts} (100%) rename modules/client/examples/01-client/{09-set-allowance.ts => 10-set-allowance.ts} (100%) rename modules/client/examples/01-client/{10-pin-metadata.ts => 11-pin-metadata.ts} (100%) rename modules/client/examples/01-client/{11-prepare-installation.ts => 12-prepare-installation.ts} (100%) rename modules/client/examples/01-client/{12-get-protocol-version.ts => 13-get-protocol-version.ts} (100%) rename modules/client/examples/01-client/{12-prepare-uninstallation.ts => 14-prepare-uninstallation.ts} (100%) create mode 100644 modules/client/test/helpers/deploy-erc1155.ts diff --git a/modules/client-common/src/types.ts b/modules/client-common/src/types.ts index 8189717b9..d2ccb6a87 100644 --- a/modules/client-common/src/types.ts +++ b/modules/client-common/src/types.ts @@ -148,6 +148,7 @@ export enum TokenType { NATIVE = "native", ERC20 = "erc20", ERC721 = "erc721", + ERC1155 = "erc1155", } /** diff --git a/modules/client/CHANGELOG.md b/modules/client/CHANGELOG.md index 2ba3bb9bf..224aecd29 100644 --- a/modules/client/CHANGELOG.md +++ b/modules/client/CHANGELOG.md @@ -20,6 +20,7 @@ TEMPLATE: ### Added - Block param on `getVotingSettings` and `getMembers` functions to allow for historical data - Support for local chains +- Support for ERC1155 deposits and withdrawals ## 1.12.0-rc1 ### Added - Support for baseMainnet network diff --git a/modules/client/examples/01-client/05-deposit-erc1155.ts b/modules/client/examples/01-client/05-deposit-erc1155.ts new file mode 100644 index 000000000..2184ea71a --- /dev/null +++ b/modules/client/examples/01-client/05-deposit-erc1155.ts @@ -0,0 +1,74 @@ +/* MARKDOWN +--- +title: Deposit ERC-1155 +--- + +### Deposit ERC-1155 Tokens to a DAO + +Deposits ERC-1155 tokens to a DAO. + +- Similar to the ERC20 deposit flow +- The `tokenAddress` field is required. This is the contract address of the ERC-1155 token. +- TokenIds is required. This is the token ID of the ERC-1155 token. +- Amounts is required. This is the amount of the ERC-1155 token to deposit. +- Supports depositing multiple ERC-1155 tokens in a single transaction. +- Calls the safeTransferFrom or safeBatchTransferFrom function of the ERC-1155 token contract. +- If only one token ID is provided, the safeTransferFrom function is called. +- If multiple token IDs are provided, the safeBatchTransferFrom function is called. +*/ + +import { + Client, + DaoDepositSteps, + DepositParams, + } from "@aragon/sdk-client"; + import { GasFeeEstimation, TokenType } from "@aragon/sdk-client-common"; + import { context } from "../index"; + + // Instantiate the general purpose client from the Aragon OSx SDK context. + const client: Client = new Client(context); + + const depositParams: DepositParams = { + daoAddressOrEns: "0x1234567890123456789012345678901234567890", // my-dao.dao.eth + tokenAddress: "0x1234567890123456789012345678901234567890", // token contract adddress + type: TokenType.ERC1155, // "erc1155" for ERC1155 token + tokenIds: [BigInt(1)], // token ID of the ERC-1155 token + amounts: [BigInt(1)], // amount of the ERC-1155 token to deposit + }; + + // Estimate how much gas the transaction will cost. + const estimatedGas: GasFeeEstimation = await client.estimation.deposit( + depositParams, + ); + console.log({ avg: estimatedGas.average, max: estimatedGas.max }); + + // Deposit the ERC1155 tokens. + const steps = client.methods.deposit(depositParams); + for await (const step of steps) { + try { + switch (step.key) { + case DaoDepositSteps.DEPOSITING: + console.log({ depositingTxHash: step.txHash }); + break; + case DaoDepositSteps.DONE: + console.log({ tokenId: step.tokenIds, amount: step.amounts }); + break; + } + } catch (err) { + console.error(err); + } + } + + /* MARKDOWN + Returns: + ```tsx + { + depositingTxHash: "0xb1c14a49...3e8620b0f5832d61c" + } + { + tokenIds: [1n], + amounts: [1n] + } + ``` + */ + \ No newline at end of file diff --git a/modules/client/examples/01-client/05-get-dao.ts b/modules/client/examples/01-client/06-get-dao.ts similarity index 100% rename from modules/client/examples/01-client/05-get-dao.ts rename to modules/client/examples/01-client/06-get-dao.ts diff --git a/modules/client/examples/01-client/06-get-daos.ts b/modules/client/examples/01-client/07-get-daos.ts similarity index 100% rename from modules/client/examples/01-client/06-get-daos.ts rename to modules/client/examples/01-client/07-get-daos.ts diff --git a/modules/client/examples/01-client/07-get-balances.ts b/modules/client/examples/01-client/08-get-balances.ts similarity index 100% rename from modules/client/examples/01-client/07-get-balances.ts rename to modules/client/examples/01-client/08-get-balances.ts diff --git a/modules/client/examples/01-client/08-get-transfers.ts b/modules/client/examples/01-client/09-get-transfers.ts similarity index 100% rename from modules/client/examples/01-client/08-get-transfers.ts rename to modules/client/examples/01-client/09-get-transfers.ts diff --git a/modules/client/examples/01-client/09-set-allowance.ts b/modules/client/examples/01-client/10-set-allowance.ts similarity index 100% rename from modules/client/examples/01-client/09-set-allowance.ts rename to modules/client/examples/01-client/10-set-allowance.ts diff --git a/modules/client/examples/01-client/10-pin-metadata.ts b/modules/client/examples/01-client/11-pin-metadata.ts similarity index 100% rename from modules/client/examples/01-client/10-pin-metadata.ts rename to modules/client/examples/01-client/11-pin-metadata.ts diff --git a/modules/client/examples/01-client/11-prepare-installation.ts b/modules/client/examples/01-client/12-prepare-installation.ts similarity index 100% rename from modules/client/examples/01-client/11-prepare-installation.ts rename to modules/client/examples/01-client/12-prepare-installation.ts diff --git a/modules/client/examples/01-client/12-get-protocol-version.ts b/modules/client/examples/01-client/13-get-protocol-version.ts similarity index 100% rename from modules/client/examples/01-client/12-get-protocol-version.ts rename to modules/client/examples/01-client/13-get-protocol-version.ts diff --git a/modules/client/examples/01-client/12-prepare-uninstallation.ts b/modules/client/examples/01-client/14-prepare-uninstallation.ts similarity index 100% rename from modules/client/examples/01-client/12-prepare-uninstallation.ts rename to modules/client/examples/01-client/14-prepare-uninstallation.ts diff --git a/modules/client/examples/05-encoders-decoders/06-withdraw.ts b/modules/client/examples/05-encoders-decoders/06-withdraw.ts index b4cdee5ca..418fb9a87 100644 --- a/modules/client/examples/05-encoders-decoders/06-withdraw.ts +++ b/modules/client/examples/05-encoders-decoders/06-withdraw.ts @@ -136,7 +136,7 @@ Returns: */ params = { - type: TokenType.ERC721, + type: TokenType.ERC721, tokenAddress: "0x1234567890123456789012345678901234567890", // ERFC721's token contract address tokenId: BigInt(10), recipientAddressOrEns: "0x1234567890123456789012345678901234567890", // the address to transfer the funds to @@ -170,3 +170,47 @@ Returns: recipientAddressOrEns: "0x1234567890123456789012345678901234567890"; } */ +/* MARKDOWN +### NFT (ERC-1155) Tokens + +#### Encoding + +*/ + +params = { + type: TokenType.ERC1155, + tokenAddress: "0x1234567890123456789012345678901234567890", // ERFC721's token contract address + tokenIds: [BigInt(10)], // array of token ids + amounts: [BigInt(20)], // array of amounts + recipientAddressOrEns: "0x1234567890123456789012345678901234567890", // the address to transfer the funds to + daoAddressOrEns: "0x1234567890123456789012345678901234567890", // the address of the DAO +}; + +const erc1155WithdrawAction: DaoAction = await client.encoding.withdrawAction( + params, +); +console.log({ erc1155WithdrawAction }); + +/* MARKDOWN +#### Decoding + +*/ + +const erc1155WithdrawDecodedParams = client.decoding.withdrawAction( + erc1155WithdrawAction.to, + erc1155WithdrawAction.value, + erc1155WithdrawAction.data, +); +console.log({ erc1155WithdrawDecodedParams }); + +/* MARKDOWN +Returns: +{ + type: TokenType.ERC1155, + tokenAddress: "0x1234567890123456789012345678901234567890", + tokenIds: [10n], + amounts: [20n], + daoAddressOrEns: "0x1234567890123456789012345678901234567890", + recipientAddressOrEns: "0x1234567890123456789012345678901234567890", +} +*/ diff --git a/modules/client/src/internal/client/decoding.ts b/modules/client/src/internal/client/decoding.ts index fc8b604a0..9112666fb 100644 --- a/modules/client/src/internal/client/decoding.ts +++ b/modules/client/src/internal/client/decoding.ts @@ -30,6 +30,7 @@ import { } from "@aragon/sdk-common"; import { abi as ERC20_ABI } from "@openzeppelin/contracts/build/contracts/ERC20.json"; import { abi as ERC721_ABI } from "@openzeppelin/contracts/build/contracts/ERC721.json"; +import { abi as ERC1155_ABI } from "@openzeppelin/contracts/build/contracts/ERC1155.json"; import { Contract } from "@ethersproject/contracts"; import { AddressZero } from "@ethersproject/constants"; import { toUtf8String } from "@ethersproject/strings"; @@ -144,13 +145,27 @@ export class ClientDecoding extends ClientCore implements IClientDecoding { { tokenStandard: TokenType.ERC20, abi: ERC20_ABI, + batch: false, function: "transfer", }, { tokenStandard: TokenType.ERC721, abi: ERC721_ABI, + batch: false, function: "safeTransferFrom(address,address,uint256)", }, + { + tokenStandard: TokenType.ERC1155, + abi: ERC1155_ABI, + batch: true, + function: "safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)", + }, + { + tokenStandard: TokenType.ERC1155, + abi: ERC1155_ABI, + batch: false, + function: "safeTransferFrom(address,address,uint256,uint256,bytes)", + } ]; for (const abiObject of abiObjects) { try { @@ -163,6 +178,7 @@ export class ClientDecoding extends ClientCore implements IClientDecoding { value, result, abiObject.tokenStandard, + abiObject.batch, ); } catch (e) { continue; diff --git a/modules/client/src/internal/client/encoding.ts b/modules/client/src/internal/client/encoding.ts index 7522db8d7..78e36e665 100644 --- a/modules/client/src/internal/client/encoding.ts +++ b/modules/client/src/internal/client/encoding.ts @@ -22,12 +22,15 @@ import { import { Contract } from "@ethersproject/contracts"; import { abi as ERC20_ABI } from "@openzeppelin/contracts/build/contracts/ERC20.json"; import { abi as ERC721_ABI } from "@openzeppelin/contracts/build/contracts/ERC721.json"; +import { abi as ERC1155_ABI } from "@openzeppelin/contracts/build/contracts/ERC1155.json"; import { hexToBytes, InvalidAddressError, InvalidAddressOrEnsError, InvalidEnsError, + InvalidParameter, NotImplementedError, + SizeMismatchError, } from "@aragon/sdk-common"; import { toUtf8Bytes } from "@ethersproject/strings"; import { IClientEncoding } from "../interfaces"; @@ -295,6 +298,51 @@ export class ClientEncoding extends ClientCore implements IClientEncoding { value: BigInt(0), data: hexToBytes(data), }; + case TokenType.ERC1155: + if (params.tokenIds.length !== params.amounts.length) { + throw new SizeMismatchError(); + } + if (params.tokenIds.length === 0 || params.amounts.length === 0) { + throw new InvalidParameter("tokenIds or amounts cannot be empty"); + } + if ( + !params.tokenAddress || !params.recipientAddressOrEns || + !params.daoAddressOrEns + ) { + throw new InvalidAddressError(); + } + iface = new Contract( + params.tokenAddress, + ERC1155_ABI, + ).interface; + if (params.tokenIds.length === 1) { + data = iface.encodeFunctionData( + "safeTransferFrom(address,address,uint256,uint256,bytes)", + [ + params.daoAddressOrEns, // from + params.recipientAddressOrEns, // to + params.tokenIds[0], // tokenId + params.amounts[0], // amount + new Uint8Array(), // data + ], + ); + } else { + data = iface.encodeFunctionData( + "safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)", + [ + params.daoAddressOrEns, // from + params.recipientAddressOrEns, // to + params.tokenIds, // tokenIds + params.amounts, // amounts + new Uint8Array(), // data + ], + ); + } + return { + to: params.tokenAddress, + value: BigInt(0), + data: hexToBytes(data), + }; default: throw new NotImplementedError("Token type not supported"); } diff --git a/modules/client/src/internal/client/estimation.ts b/modules/client/src/internal/client/estimation.ts index c4cedd87b..777d5f5da 100644 --- a/modules/client/src/internal/client/estimation.ts +++ b/modules/client/src/internal/client/estimation.ts @@ -1,14 +1,13 @@ import { - DAO__factory, DAOFactory, DAOFactory__factory, PluginRepo__factory, } from "@aragon/osx-ethers"; import { - DepositNativeTokenError, InvalidAddressOrEnsError, InvalidSubdomainError, NoProviderError, + NotImplementedError, } from "@aragon/sdk-common"; import { AddressZero } from "@ethersproject/constants"; import { Contract } from "@ethersproject/contracts"; @@ -18,7 +17,11 @@ import { DepositParams, SetAllowanceParams, } from "../../types"; -import { unwrapDepositParams } from "../utils"; +import { + estimateErc1155Deposit, + estimateErc20Deposit, + estimateErc721Deposit, +} from "../utils"; import { isAddress } from "@ethersproject/address"; import { toUtf8Bytes } from "@ethersproject/strings"; import { IClientEstimation } from "../interfaces"; @@ -29,6 +32,7 @@ import { PrepareInstallationParams, TokenType, } from "@aragon/sdk-client-common"; +import { BigNumber } from "@ethersproject/bignumber"; /** * Estimation module the SDK Generic Client @@ -95,32 +99,30 @@ export class ClientEstimation extends ClientCore implements IClientEstimation { * @return {*} {Promise} * @memberof ClientEstimation */ - public deposit( + public async deposit( params: DepositParams, ): Promise { const signer = this.web3.getConnectedSigner(); - - if (params.type !== TokenType.NATIVE && params.type !== TokenType.ERC20) { - throw new DepositNativeTokenError(); - } - - const [daoAddress, amount, tokenAddress, reference] = unwrapDepositParams( - params, - ); - - const daoInstance = DAO__factory.connect(daoAddress, signer); - - const override: { value?: bigint } = {}; - if (tokenAddress === AddressZero) { - override.value = amount; + let estimation: BigNumber; + switch (params.type) { + case TokenType.NATIVE: + case TokenType.ERC20: + estimation = await estimateErc20Deposit(signer, params); + break; + case TokenType.ERC721: + estimation = await estimateErc721Deposit(signer, params); + break; + case TokenType.ERC1155: + estimation = await estimateErc1155Deposit(signer, params); + break; + default: + throw new NotImplementedError( + "Token type not valid", + ); } - - return daoInstance.estimateGas - .deposit(tokenAddress, amount, reference, override) - .then((gasLimit) => { - return this.web3.getApproximateGasFee(gasLimit.toBigInt()); - }); + return this.web3.getApproximateGasFee(estimation.toBigInt()); } + /** * Estimates the gas fee of updating the allowance of an ERC20 token * diff --git a/modules/client/src/internal/client/methods.ts b/modules/client/src/internal/client/methods.ts index 722ecaa46..b55eb8e57 100644 --- a/modules/client/src/internal/client/methods.ts +++ b/modules/client/src/internal/client/methods.ts @@ -16,6 +16,7 @@ import { InvalidAddressOrEnsError, InvalidCidError, InvalidEnsError, + InvalidParameter, IpfsPinError, MissingExecPermissionError, NoProviderError, @@ -23,6 +24,7 @@ import { PluginUninstallationPreparationError, promiseWithTimeout, resolveIpfsCid, + SizeMismatchError, UpdateAllowanceError, } from "@aragon/sdk-common"; @@ -32,6 +34,7 @@ import { AddressZero } from "@ethersproject/constants"; import { Contract, ContractTransaction } from "@ethersproject/contracts"; import { abi as ERC20_ABI } from "@openzeppelin/contracts/build/contracts/ERC20.json"; import { abi as ERC721_ABI } from "@openzeppelin/contracts/build/contracts/ERC721.json"; +import { abi as ERC1155_ABI } from "@openzeppelin/contracts/build/contracts/ERC1155.json"; import { QueryDao, QueryDaos, @@ -55,6 +58,7 @@ import { DaoMetadata, DaoQueryParams, DaoSortBy, + DepositErc1155Params, DepositErc20Params, DepositErc721Params, DepositEthParams, @@ -283,12 +287,18 @@ export class ClientMethods extends ClientCore implements IClientMethods { params: DepositParams, ): AsyncGenerator { switch (params.type) { - case TokenType.ERC20 || TokenType.NATIVE: + case TokenType.NATIVE: + yield* this.depositNative(params); + break; + case TokenType.ERC20: yield* this.depositErc20(params); break; case TokenType.ERC721: yield* this.depositErc721(params); break; + case TokenType.ERC1155: + yield* this.depositErc1155(params); + break; default: throw new NotImplementedError( "Token type not valid, use transfer function instead", @@ -296,64 +306,78 @@ export class ClientMethods extends ClientCore implements IClientMethods { } } - private async *depositErc20( - params: DepositErc20Params | DepositEthParams, + private async *depositNative( + params: DepositEthParams, ): AsyncGenerator { const signer = this.web3.getConnectedSigner(); - if ( - params.type === TokenType.ERC20 && params.tokenAddress && - params.tokenAddress !== AddressZero - ) { - const { tokenAddress, daoAddressOrEns } = params; - // check current allowance - const tokenContract = new Contract( - tokenAddress, - ERC20_ABI, - signer, - ); - const currentAllowance = await tokenContract.allowance( - await signer.getAddress(), - daoAddressOrEns, - ); - yield { - key: DaoDepositSteps.CHECKED_ALLOWANCE, - allowance: currentAllowance.toBigInt(), - }; - // if its lower than the needed, set it to the correct one - if (currentAllowance.lt(params.amount)) { - // If the target is an ERC20 token, ensure that the amount can be transferred - // Relay the yield steps to the caller as they are received - yield* this.setAllowance( - { - amount: params.amount, - spender: params.daoAddressOrEns, - tokenAddress: params.tokenAddress, - }, - ); - } + const { daoAddressOrEns, amount } = params; + const override: { value?: bigint } = { value: params.amount }; + const daoInstance = DAO__factory.connect(daoAddressOrEns, signer); + + const tx = await daoInstance.deposit( + AddressZero, + amount, + "", + override, + ); + yield { key: DaoDepositSteps.DEPOSITING, txHash: tx.hash }; + + const cr = await tx.wait(); + const log = findLog(cr, daoInstance.interface, "Deposited"); + if (!log) { + throw new FailedDepositError(); } - let tokenAddress = AddressZero; - if (params.type === TokenType.ERC20) { - tokenAddress = params.tokenAddress; + + const daoInterface = DAO__factory.createInterface(); + const parsedLog = daoInterface.parseLog(log); + + if (!amount.toString() === parsedLog.args["amount"]) { + throw new AmountMismatchError( + amount, + parsedLog.args["amount"].toBigInt(), + ); } - const { - amount, + yield { key: DaoDepositSteps.DONE, amount: amount }; + } + + private async *depositErc20( + params: DepositErc20Params, + ): AsyncGenerator { + const signer = this.web3.getConnectedSigner(); + const { tokenAddress, daoAddressOrEns, amount } = params; + // check current allowance + const tokenContract = new Contract( + tokenAddress, + ERC20_ABI, + signer, + ); + const currentAllowance = await tokenContract.allowance( + await signer.getAddress(), daoAddressOrEns, - } = params; + ); + yield { + key: DaoDepositSteps.CHECKED_ALLOWANCE, + allowance: currentAllowance.toBigInt(), + }; + // if its lower than the needed, set it to the correct one + if (currentAllowance.lt(params.amount)) { + // If the target is an ERC20 token, ensure that the amount can be transferred + // Relay the yield steps to the caller as they are received + yield* this.setAllowance( + { + amount: params.amount, + spender: params.daoAddressOrEns, + tokenAddress: params.tokenAddress, + }, + ); + } // Doing the transfer const daoInstance = DAO__factory.connect(daoAddressOrEns, signer); - const override: { value?: bigint } = {}; - - if (tokenAddress === AddressZero) { - // Ether - override.value = params.amount; - } const tx = await daoInstance.deposit( tokenAddress, amount, "", - override, ); yield { key: DaoDepositSteps.DEPOSITING, txHash: tx.hash }; @@ -412,6 +436,74 @@ export class ClientMethods extends ClientCore implements IClientMethods { }; } + private async *depositErc1155( + params: DepositErc1155Params, + ): AsyncGenerator { + // if length is 0, throw + if (!params.tokenIds.length || !params.amounts.length) { + throw new InvalidParameter("tokenIds or amounts cannot be empty"); + } + // if tokenIds and amounts length are different, throw + if ( + params.tokenIds.length !== params.amounts.length + ) { + throw new SizeMismatchError(); + } + + const signer = this.web3.getConnectedSigner(); + const erc1155Contract = new Contract( + params.tokenAddress, + ERC1155_ABI, + signer, + ); + + let tx: ContractTransaction, logName: string, logArg: string; + if (params.tokenIds.length === 1) { + tx = await erc1155Contract + ["safeTransferFrom(address,address,uint256,uint256,bytes)"]( + await signer.getAddress(), + params.daoAddressOrEns, + params.tokenIds[0], + params.amounts[0], + new Uint8Array([]), + ); + logName = "TransferSingle"; + logArg = "id"; + } else { + tx = await erc1155Contract + ["safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"]( + await signer.getAddress(), + params.daoAddressOrEns, + params.tokenIds, + params.amounts, + new Uint8Array([]), + ); + logName = "TransferBatch"; + logArg = "ids"; + } + + const cr = await tx.wait(); + + const log = findLog(cr, erc1155Contract.interface, logName); + + if (!log) { + throw new FailedDepositError(); + } + + const parsedLog = erc1155Contract.interface.parseLog(log); + if ( + !parsedLog.args[logArg] || + parsedLog.args[logArg].toString() !== params.tokenIds.toString() + ) { + throw new FailedDepositError(); + } + yield { + key: DaoDepositSteps.DONE, + tokenIds: params.tokenIds, + amounts: params.amounts, + }; + } + /** * Checks if the allowance is enough and updates it * diff --git a/modules/client/src/internal/utils.ts b/modules/client/src/internal/utils.ts index 82b698a16..9f35e601b 100644 --- a/modules/client/src/internal/utils.ts +++ b/modules/client/src/internal/utils.ts @@ -4,7 +4,9 @@ import { DaoDetails, DaoListItem, DaoMetadata, + DepositErc1155Params, DepositErc20Params, + DepositErc721Params, DepositEthParams, GrantPermissionDecodedParams, GrantPermissionParams, @@ -39,14 +41,19 @@ import { defaultAbiCoder, Result } from "@ethersproject/abi"; import { keccak256 } from "@ethersproject/keccak256"; import { toUtf8Bytes } from "@ethersproject/strings"; import { AddressZero } from "@ethersproject/constants"; -import { PluginSetupProcessor } from "@aragon/osx-ethers"; +import { DAO__factory, PluginSetupProcessor } from "@aragon/osx-ethers"; import { PermissionIds } from "../constants"; import { ApplyInstallationParams, DecodedApplyInstallationParams, TokenType, } from "@aragon/sdk-client-common"; -import { NotImplementedError } from "@aragon/sdk-common"; +import { InvalidParameter, NotImplementedError, SizeMismatchError } from "@aragon/sdk-common"; +import { Signer } from "@ethersproject/abstract-signer"; +import { Contract } from "@ethersproject/contracts"; +import { BigNumber } from "@ethersproject/bignumber"; +import { abi as ERC721_ABI } from "@openzeppelin/contracts/build/contracts/ERC721.json"; +import { abi as ERC1155_ABI } from "@openzeppelin/contracts/build/contracts/ERC1155.json"; export function unwrapDepositParams( params: DepositEthParams | DepositErc20Params, @@ -383,6 +390,7 @@ export function withdrawParamsFromContract( _value: bigint, result: Result, tokenStandard: TokenType, + isBatch: boolean, ): WithdrawParams { switch (tokenStandard) { case TokenType.ERC20: @@ -400,8 +408,100 @@ export function withdrawParamsFromContract( tokenId: BigInt(result[2]), daoAddressOrEns: result[0], }; + case TokenType.ERC1155: + let tokenIds: bigint[], amounts: bigint[]; + if (isBatch) { + tokenIds = result[2].map((id: string) => BigInt(id)); + amounts = result[3].map((amount: string) => BigInt(amount)); + } else { + tokenIds = [BigInt(result[2])]; + amounts = [BigInt(result[3])]; + } + return { + type: TokenType.ERC1155, + tokenAddress: to, + recipientAddressOrEns: result[1], + tokenIds, + amounts, + daoAddressOrEns: result[0], + }; } - - // TODO Add ERC1155 throw new NotImplementedError("Token standard not supported"); } + +export async function estimateErc20Deposit( + signer: Signer, + params: DepositErc20Params | DepositEthParams, +): Promise { + let tokenAddress; + if (params.type === TokenType.NATIVE) { + tokenAddress = AddressZero; + } else { + tokenAddress = params.tokenAddress; + } + const daoInstance = DAO__factory.connect(params.daoAddressOrEns, signer); + return await daoInstance.estimateGas.deposit( + tokenAddress, + params.amount, + "", + ); +} + +export async function estimateErc721Deposit( + signer: Signer, + params: DepositErc721Params, +): Promise { + const erc721Contract = new Contract( + params.tokenAddress, + ERC721_ABI, + signer, + ); + return erc721Contract.estimateGas + ["safeTransferFrom(address,address,uint256)"]( + await signer.getAddress(), + params.daoAddressOrEns, + params.tokenId, + ); +} + +export async function estimateErc1155Deposit( + signer: Signer, + params: DepositErc1155Params, +): Promise { + // if length is 0, throw + if (!params.tokenIds.length || !params.amounts.length) { + throw new InvalidParameter("tokenIds or amounts cannot be empty"); + } + // if tokenIds and amounts length are different, throw + if ( + params.tokenIds.length !== params.amounts.length + ) { + throw new SizeMismatchError(); + } + const erc1155Contract = new Contract( + params.tokenAddress, + ERC1155_ABI, + signer, + ); + let estimation: BigNumber; + if (params.tokenIds.length === 1) { + estimation = await erc1155Contract.estimateGas + ["safeTransferFrom(address,address,uint256,uint256,bytes)"]( + await signer.getAddress(), + params.daoAddressOrEns, + params.tokenIds[0], + params.amounts[0], + new Uint8Array(0), + ); + } else { + estimation = await erc1155Contract.estimateGas + ["safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)"]( + await signer.getAddress(), + params.daoAddressOrEns, + params.tokenIds, + params.amounts, + new Uint8Array(0), + ); + } + return estimation; +} diff --git a/modules/client/src/types.ts b/modules/client/src/types.ts index 3924f2b9e..6c6d51e48 100644 --- a/modules/client/src/types.ts +++ b/modules/client/src/types.ts @@ -143,10 +143,18 @@ export type DepositErc721Params = DepositBaseParams & { tokenId: bigint; }; +export type DepositErc1155Params = DepositBaseParams & { + type: TokenType.ERC1155; + tokenAddress: string; + tokenIds: bigint[]; + amounts: bigint[]; +}; + export type DepositParams = | DepositEthParams | DepositErc20Params - | DepositErc721Params; + | DepositErc721Params + | DepositErc1155Params; export enum DaoDepositSteps { CHECKED_ALLOWANCE = "checkedAllowance", @@ -158,7 +166,13 @@ export type DaoDepositStepValue = | SetAllowanceStepValue | { key: DaoDepositSteps.CHECKED_ALLOWANCE; allowance: bigint } | { key: DaoDepositSteps.DEPOSITING; txHash: string } - | { key: DaoDepositSteps.DONE; amount?: bigint, tokenId?: bigint } + | { + key: DaoDepositSteps.DONE; + amount?: bigint; + tokenId?: bigint; + tokenIds?: bigint[]; + amounts?: bigint[]; + }; /* Withdrawals */ type WithdrawParamsBase = { @@ -183,10 +197,19 @@ type WithdrawErc721Params = WithdrawParamsBase & { daoAddressOrEns: string; }; +type WithdrawErc1155Params = WithdrawParamsBase & { + type: TokenType.ERC1155; + daoAddressOrEns: string; + tokenAddress: string; + tokenIds: bigint[]; + amounts: bigint[]; +}; + export type WithdrawParams = | WithdrawEthParams | WithdrawErc20Params - | WithdrawErc721Params; + | WithdrawErc721Params + | WithdrawErc1155Params; /* Balances */ type AssetBalanceBase = { diff --git a/modules/client/test/helpers/deploy-erc1155.ts b/modules/client/test/helpers/deploy-erc1155.ts new file mode 100644 index 000000000..54487458b --- /dev/null +++ b/modules/client/test/helpers/deploy-erc1155.ts @@ -0,0 +1,25 @@ +import { Client } from "../../src"; +import { contextParamsLocalChain } from "../integration/constants"; +import { Context } from "@aragon/sdk-client-common"; +import { ContractFactory } from "@ethersproject/contracts"; +import { + abi as ERC1155_ABI, + bytecode as ERC1155_ABI_BYTECODE, +} from "@openzeppelin/contracts/build/contracts/ERC1155PresetMinterPauser.json"; + +export async function deployErc1155() { + const client = new Client(new Context(contextParamsLocalChain)); + const signer = client.web3.getConnectedSigner(); + + // the bytecode automatically mints 10 of tokenID 0 to the deployer + const factory = new ContractFactory( + ERC1155_ABI, + ERC1155_ABI_BYTECODE, + signer, + ); + const contract = await factory.deploy("https://example.org/{id}.json"); + await contract.mint(signer.getAddress(), 0, 10, "0x"); + await contract.mint(signer.getAddress(), 1, 10, "0x"); + await contract.mint(signer.getAddress(), 2, 10, "0x"); + return contract; +} diff --git a/modules/client/test/helpers/deploy-erc20.ts b/modules/client/test/helpers/deploy-erc20.ts index e39394974..e0290072a 100644 --- a/modules/client/test/helpers/deploy-erc20.ts +++ b/modules/client/test/helpers/deploy-erc20.ts @@ -2,19 +2,23 @@ import { ContractFactory } from "@ethersproject/contracts"; import { Client } from "../../src"; import { contextParamsLocalChain } from "../integration/constants"; import { Context } from "@aragon/sdk-client-common"; -import { abi as ERC20_ABI } from "@openzeppelin/contracts/build/contracts/ERC20.json"; +import { + abi as ERC20_ABI, + bytecode as ERC20_ABI_BYTECODE, +} from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -export function deployErc20() { +export async function deployErc20() { const client = new Client(new Context(contextParamsLocalChain)); - const ercBytecode = - "0x608060405234801561001057600080fd5b5068056bc75e2d63100000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555068056bc75e2d63100000600081905550610d828061007d6000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461013457806370a082311461015257806395d89b4114610182578063a9059cbb146101a0578063dd62ed3e146101d057610093565b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100e657806323b872dd14610104575b600080fd5b6100a0610200565b6040516100ad9190610ac1565b60405180910390f35b6100d060048036038101906100cb91906109ba565b610239565b6040516100dd9190610aa6565b60405180910390f35b6100ee61032b565b6040516100fb9190610b23565b60405180910390f35b61011e60048036038101906101199190610967565b610331565b60405161012b9190610aa6565b60405180910390f35b61013c610623565b6040516101499190610b3e565b60405180910390f35b61016c600480360381019061016791906108fa565b610628565b6040516101799190610b23565b60405180910390f35b61018a610671565b6040516101979190610ac1565b60405180910390f35b6101ba60048036038101906101b591906109ba565b6106aa565b6040516101c79190610aa6565b60405180910390f35b6101ea60048036038101906101e59190610927565b610849565b6040516101f79190610b23565b60405180910390f35b6040518060400160405280600581526020017f546f6b656e00000000000000000000000000000000000000000000000000000081525081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516103199190610b23565b60405180910390a36001905092915050565b60005481565b600081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156103f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103e990610ae3565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610474576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161046b90610b03565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546104c39190610bcb565b9250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546105199190610b75565b9250508190555081600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546105ac9190610bcb565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516106109190610b23565b60405180910390a3600190509392505050565b601281565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6040518060400160405280600381526020017f544f4b000000000000000000000000000000000000000000000000000000000081525081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561072e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072590610b03565b60405180910390fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461077d9190610bcb565b9250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107d39190610b75565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516108379190610b23565b60405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b6000813590506108df81610d1e565b92915050565b6000813590506108f481610d35565b92915050565b6000602082840312156109105761090f610cb6565b5b600061091e848285016108d0565b91505092915050565b6000806040838503121561093e5761093d610cb6565b5b600061094c858286016108d0565b925050602061095d858286016108d0565b9150509250929050565b6000806000606084860312156109805761097f610cb6565b5b600061098e868287016108d0565b935050602061099f868287016108d0565b92505060406109b0868287016108e5565b9150509250925092565b600080604083850312156109d1576109d0610cb6565b5b60006109df858286016108d0565b92505060206109f0858286016108e5565b9150509250929050565b610a0381610c11565b82525050565b6000610a1482610b59565b610a1e8185610b64565b9350610a2e818560208601610c54565b610a3781610cbb565b840191505092915050565b6000610a4f601683610b64565b9150610a5a82610ccc565b602082019050919050565b6000610a72601483610b64565b9150610a7d82610cf5565b602082019050919050565b610a9181610c3d565b82525050565b610aa081610c47565b82525050565b6000602082019050610abb60008301846109fa565b92915050565b60006020820190508181036000830152610adb8184610a09565b905092915050565b60006020820190508181036000830152610afc81610a42565b9050919050565b60006020820190508181036000830152610b1c81610a65565b9050919050565b6000602082019050610b386000830184610a88565b92915050565b6000602082019050610b536000830184610a97565b92915050565b600081519050919050565b600082825260208201905092915050565b6000610b8082610c3d565b9150610b8b83610c3d565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610bc057610bbf610c87565b5b828201905092915050565b6000610bd682610c3d565b9150610be183610c3d565b925082821015610bf457610bf3610c87565b5b828203905092915050565b6000610c0a82610c1d565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60005b83811015610c72578082015181840152602081019050610c57565b83811115610c81576000848401525b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b6000601f19601f8301169050919050565b7f496e73756666696369656e7420616c6c6f77616e636500000000000000000000600082015250565b7f496e73756666696369656e742062616c616e6365000000000000000000000000600082015250565b610d2781610bff565b8114610d3257600080fd5b50565b610d3e81610c3d565b8114610d4957600080fd5b5056fea26469706673582212205e600dc9189ad70cc4361b1af1ad82c2d22a08e0a2d00a9de6ce1c7f496787e664736f6c63430008070033"; - + const signer = client.web3.getConnectedSigner(); + const factory = new ContractFactory( ERC20_ABI, - ercBytecode, + ERC20_ABI_BYTECODE, client.web3.getConnectedSigner(), ); + const contract = await factory.deploy("Test", "TOK"); + await contract.mint(signer.getAddress(), "10000000000000000000"); // If your contract requires constructor args, you can specify them here - return factory.deploy("Test", "TOK"); + return contract; } diff --git a/modules/client/test/helpers/deploy-erc721.ts b/modules/client/test/helpers/deploy-erc721.ts index a3952c1ba..fad7b5680 100644 --- a/modules/client/test/helpers/deploy-erc721.ts +++ b/modules/client/test/helpers/deploy-erc721.ts @@ -2,18 +2,21 @@ import { Client } from "../../src"; import { contextParamsLocalChain } from "../integration/constants"; import { Context } from "@aragon/sdk-client-common"; import { ContractFactory } from "@ethersproject/contracts"; -import { abi as ERC721_ABI } from "@openzeppelin/contracts/build/contracts/ERC721.json"; +import { + abi as ERC721_ABI, + bytecode as ERC721_ABI_BYTECODE, +} from "@openzeppelin/contracts/build/contracts/ERC721PresetMinterPauserAutoId.json"; -export function deployErc721() { +export async function deployErc721() { const client = new Client(new Context(contextParamsLocalChain)); - const erc721Bytecode = - "0x60806040523480156200001157600080fd5b5060405162002f5738038062002f5783398181016040528101906200003791906200074d565b818181600090816200004a919062000a1d565b5080600190816200005c919062000a1d565b505050620000723360006200007a60201b60201c565b505062000e27565b6200009c828260405180602001604052806000815250620000a060201b60201c565b5050565b620000b283836200010e60201b60201c565b620000c760008484846200035460201b60201c565b62000109576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001009062000b8b565b60405180910390fd5b505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160362000180576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001779062000bfd565b60405180910390fd5b6200019181620004fd60201b60201c565b15620001d4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001cb9062000c6f565b60405180910390fd5b620001ea6000838360016200054660201b60201c565b620001fb81620004fd60201b60201c565b156200023e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620002359062000c6f565b60405180910390fd5b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4620003506000838360016200054c60201b60201c565b5050565b6000620003828473ffffffffffffffffffffffffffffffffffffffff166200055260201b620009501760201c565b15620004f0578373ffffffffffffffffffffffffffffffffffffffff1663150b7a02620003b46200057560201b60201c565b8786866040518563ffffffff1660e01b8152600401620003d8949392919062000d44565b6020604051808303816000875af19250505080156200041757506040513d601f19601f8201168201806040525081019062000414919062000df5565b60015b6200049f573d80600081146200044a576040519150601f19603f3d011682016040523d82523d6000602084013e6200044f565b606091505b50600081510362000497576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200048e9062000b8b565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614915050620004f5565b600190505b949350505050565b60008073ffffffffffffffffffffffffffffffffffffffff1662000527836200057d60201b60201c565b73ffffffffffffffffffffffffffffffffffffffff1614159050919050565b50505050565b50505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b600033905090565b60006002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6200062382620005d8565b810181811067ffffffffffffffff82111715620006455762000644620005e9565b5b80604052505050565b60006200065a620005ba565b905062000668828262000618565b919050565b600067ffffffffffffffff8211156200068b576200068a620005e9565b5b6200069682620005d8565b9050602081019050919050565b60005b83811015620006c3578082015181840152602081019050620006a6565b60008484015250505050565b6000620006e6620006e0846200066d565b6200064e565b905082815260208101848484011115620007055762000704620005d3565b5b62000712848285620006a3565b509392505050565b600082601f830112620007325762000731620005ce565b5b815162000744848260208601620006cf565b91505092915050565b60008060408385031215620007675762000766620005c4565b5b600083015167ffffffffffffffff811115620007885762000787620005c9565b5b62000796858286016200071a565b925050602083015167ffffffffffffffff811115620007ba57620007b9620005c9565b5b620007c8858286016200071a565b9150509250929050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200082557607f821691505b6020821081036200083b576200083a620007dd565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620008a57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000866565b620008b1868362000866565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620008fe620008f8620008f284620008c9565b620008d3565b620008c9565b9050919050565b6000819050919050565b6200091a83620008dd565b62000932620009298262000905565b84845462000873565b825550505050565b600090565b620009496200093a565b620009568184846200090f565b505050565b5b818110156200097e57620009726000826200093f565b6001810190506200095c565b5050565b601f821115620009cd57620009978162000841565b620009a28462000856565b81016020851015620009b2578190505b620009ca620009c18562000856565b8301826200095b565b50505b505050565b600082821c905092915050565b6000620009f260001984600802620009d2565b1980831691505092915050565b600062000a0d8383620009df565b9150826002028217905092915050565b62000a2882620007d2565b67ffffffffffffffff81111562000a445762000a43620005e9565b5b62000a5082546200080c565b62000a5d82828562000982565b600060209050601f83116001811462000a95576000841562000a80578287015190505b62000a8c8582620009ff565b86555062000afc565b601f19841662000aa58662000841565b60005b8281101562000acf5784890151825560018201915060208501945060208101905062000aa8565b8683101562000aef578489015162000aeb601f891682620009df565b8355505b6001600288020188555050505b505050505050565b600082825260208201905092915050565b7f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008201527f63656976657220696d706c656d656e7465720000000000000000000000000000602082015250565b600062000b7360328362000b04565b915062000b808262000b15565b604082019050919050565b6000602082019050818103600083015262000ba68162000b64565b9050919050565b7f4552433732313a206d696e7420746f20746865207a65726f2061646472657373600082015250565b600062000be560208362000b04565b915062000bf28262000bad565b602082019050919050565b6000602082019050818103600083015262000c188162000bd6565b9050919050565b7f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000600082015250565b600062000c57601c8362000b04565b915062000c648262000c1f565b602082019050919050565b6000602082019050818103600083015262000c8a8162000c48565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600062000cbe8262000c91565b9050919050565b62000cd08162000cb1565b82525050565b62000ce181620008c9565b82525050565b600081519050919050565b600082825260208201905092915050565b600062000d108262000ce7565b62000d1c818562000cf2565b935062000d2e818560208601620006a3565b62000d3981620005d8565b840191505092915050565b600060808201905062000d5b600083018762000cc5565b62000d6a602083018662000cc5565b62000d79604083018562000cd6565b818103606083015262000d8d818462000d03565b905095945050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b62000dcf8162000d98565b811462000ddb57600080fd5b50565b60008151905062000def8162000dc4565b92915050565b60006020828403121562000e0e5762000e0d620005c4565b5b600062000e1e8482850162000dde565b91505092915050565b6121208062000e376000396000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c80636352211e1161008c578063a22cb46511610066578063a22cb46514610224578063b88d4fde14610240578063c87b56dd1461025c578063e985e9c51461028c576100cf565b80636352211e146101a657806370a08231146101d657806395d89b4114610206576100cf565b806301ffc9a7146100d457806306fdde0314610104578063081812fc14610122578063095ea7b31461015257806323b872dd1461016e57806342842e0e1461018a575b600080fd5b6100ee60048036038101906100e991906114f4565b6102bc565b6040516100fb919061153c565b60405180910390f35b61010c61039e565b60405161011991906115e7565b60405180910390f35b61013c6004803603810190610137919061163f565b610430565b60405161014991906116ad565b60405180910390f35b61016c600480360381019061016791906116f4565b610476565b005b61018860048036038101906101839190611734565b61058d565b005b6101a4600480360381019061019f9190611734565b6105ed565b005b6101c060048036038101906101bb919061163f565b61060d565b6040516101cd91906116ad565b60405180910390f35b6101f060048036038101906101eb9190611787565b610693565b6040516101fd91906117c3565b60405180910390f35b61020e61074a565b60405161021b91906115e7565b60405180910390f35b61023e6004803603810190610239919061180a565b6107dc565b005b61025a6004803603810190610255919061197f565b6107f2565b005b6102766004803603810190610271919061163f565b610854565b60405161028391906115e7565b60405180910390f35b6102a660048036038101906102a19190611a02565b6108bc565b6040516102b3919061153c565b60405180910390f35b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061038757507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b80610397575061039682610973565b5b9050919050565b6060600080546103ad90611a71565b80601f01602080910402602001604051908101604052809291908181526020018280546103d990611a71565b80156104265780601f106103fb57610100808354040283529160200191610426565b820191906000526020600020905b81548152906001019060200180831161040957829003601f168201915b5050505050905090565b600061043b826109dd565b6004600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b60006104818261060d565b90508073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036104f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104e890611b14565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff16610510610a28565b73ffffffffffffffffffffffffffffffffffffffff16148061053f575061053e81610539610a28565b6108bc565b5b61057e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057590611ba6565b60405180910390fd5b6105888383610a30565b505050565b61059e610598610a28565b82610ae9565b6105dd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105d490611c38565b60405180910390fd5b6105e8838383610b7e565b505050565b610608838383604051806020016040528060008152506107f2565b505050565b60008061061983610e77565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361068a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161068190611ca4565b60405180910390fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610703576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106fa90611d36565b60405180910390fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606001805461075990611a71565b80601f016020809104026020016040519081016040528092919081815260200182805461078590611a71565b80156107d25780601f106107a7576101008083540402835291602001916107d2565b820191906000526020600020905b8154815290600101906020018083116107b557829003601f168201915b5050505050905090565b6107ee6107e7610a28565b8383610eb4565b5050565b6108036107fd610a28565b83610ae9565b610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161083990611c38565b60405180910390fd5b61084e84848484611020565b50505050565b606061085f826109dd565b600061086961107c565b9050600081511161088957604051806020016040528060008152506108b4565b8061089384611093565b6040516020016108a4929190611d92565b6040516020818303038152906040525b915050919050565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b6109e681611161565b610a25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a1c90611ca4565b60405180910390fd5b50565b600033905090565b816004600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff16610aa38361060d565b73ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600080610af58361060d565b90508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161480610b375750610b3681856108bc565b5b80610b7557508373ffffffffffffffffffffffffffffffffffffffff16610b5d84610430565b73ffffffffffffffffffffffffffffffffffffffff16145b91505092915050565b8273ffffffffffffffffffffffffffffffffffffffff16610b9e8261060d565b73ffffffffffffffffffffffffffffffffffffffff1614610bf4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610beb90611e28565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610c63576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c5a90611eba565b60405180910390fd5b610c7083838360016111a2565b8273ffffffffffffffffffffffffffffffffffffffff16610c908261060d565b73ffffffffffffffffffffffffffffffffffffffff1614610ce6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cdd90611e28565b60405180910390fd5b6004600082815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055506001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540192505081905550816002600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4610e7283838360016111a8565b505050565b60006002600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610f22576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f1990611f26565b60405180910390fd5b80600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051611013919061153c565b60405180910390a3505050565b61102b848484610b7e565b611037848484846111ae565b611076576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161106d90611fb8565b60405180910390fd5b50505050565b606060405180602001604052806000815250905090565b6060600060016110a284611335565b01905060008167ffffffffffffffff8111156110c1576110c0611854565b5b6040519080825280601f01601f1916602001820160405280156110f35781602001600182028036833780820191505090505b509050600082602001820190505b600115611156578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a858161114a57611149611fd8565b5b04945060008503611101575b819350505050919050565b60008073ffffffffffffffffffffffffffffffffffffffff1661118383610e77565b73ffffffffffffffffffffffffffffffffffffffff1614159050919050565b50505050565b50505050565b60006111cf8473ffffffffffffffffffffffffffffffffffffffff16610950565b15611328578373ffffffffffffffffffffffffffffffffffffffff1663150b7a026111f8610a28565b8786866040518563ffffffff1660e01b815260040161121a949392919061205c565b6020604051808303816000875af192505050801561125657506040513d601f19601f8201168201806040525081019061125391906120bd565b60015b6112d8573d8060008114611286576040519150601f19603f3d011682016040523d82523d6000602084013e61128b565b606091505b5060008151036112d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112c790611fb8565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161491505061132d565b600190505b949350505050565b600080600090507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611393577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161138957611388611fd8565b5b0492506040810190505b6d04ee2d6d415b85acef810000000083106113d0576d04ee2d6d415b85acef810000000083816113c6576113c5611fd8565b5b0492506020810190505b662386f26fc1000083106113ff57662386f26fc1000083816113f5576113f4611fd8565b5b0492506010810190505b6305f5e1008310611428576305f5e100838161141e5761141d611fd8565b5b0492506008810190505b612710831061144d57612710838161144357611442611fd8565b5b0492506004810190505b60648310611470576064838161146657611465611fd8565b5b0492506002810190505b600a831061147f576001810190505b80915050919050565b6000604051905090565b600080fd5b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6114d18161149c565b81146114dc57600080fd5b50565b6000813590506114ee816114c8565b92915050565b60006020828403121561150a57611509611492565b5b6000611518848285016114df565b91505092915050565b60008115159050919050565b61153681611521565b82525050565b6000602082019050611551600083018461152d565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611591578082015181840152602081019050611576565b60008484015250505050565b6000601f19601f8301169050919050565b60006115b982611557565b6115c38185611562565b93506115d3818560208601611573565b6115dc8161159d565b840191505092915050565b6000602082019050818103600083015261160181846115ae565b905092915050565b6000819050919050565b61161c81611609565b811461162757600080fd5b50565b60008135905061163981611613565b92915050565b60006020828403121561165557611654611492565b5b60006116638482850161162a565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006116978261166c565b9050919050565b6116a78161168c565b82525050565b60006020820190506116c2600083018461169e565b92915050565b6116d18161168c565b81146116dc57600080fd5b50565b6000813590506116ee816116c8565b92915050565b6000806040838503121561170b5761170a611492565b5b6000611719858286016116df565b925050602061172a8582860161162a565b9150509250929050565b60008060006060848603121561174d5761174c611492565b5b600061175b868287016116df565b935050602061176c868287016116df565b925050604061177d8682870161162a565b9150509250925092565b60006020828403121561179d5761179c611492565b5b60006117ab848285016116df565b91505092915050565b6117bd81611609565b82525050565b60006020820190506117d860008301846117b4565b92915050565b6117e781611521565b81146117f257600080fd5b50565b600081359050611804816117de565b92915050565b6000806040838503121561182157611820611492565b5b600061182f858286016116df565b9250506020611840858286016117f5565b9150509250929050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61188c8261159d565b810181811067ffffffffffffffff821117156118ab576118aa611854565b5b80604052505050565b60006118be611488565b90506118ca8282611883565b919050565b600067ffffffffffffffff8211156118ea576118e9611854565b5b6118f38261159d565b9050602081019050919050565b82818337600083830152505050565b600061192261191d846118cf565b6118b4565b90508281526020810184848401111561193e5761193d61184f565b5b611949848285611900565b509392505050565b600082601f8301126119665761196561184a565b5b813561197684826020860161190f565b91505092915050565b6000806000806080858703121561199957611998611492565b5b60006119a7878288016116df565b94505060206119b8878288016116df565b93505060406119c98782880161162a565b925050606085013567ffffffffffffffff8111156119ea576119e9611497565b5b6119f687828801611951565b91505092959194509250565b60008060408385031215611a1957611a18611492565b5b6000611a27858286016116df565b9250506020611a38858286016116df565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680611a8957607f821691505b602082108103611a9c57611a9b611a42565b5b50919050565b7f4552433732313a20617070726f76616c20746f2063757272656e74206f776e6560008201527f7200000000000000000000000000000000000000000000000000000000000000602082015250565b6000611afe602183611562565b9150611b0982611aa2565b604082019050919050565b60006020820190508181036000830152611b2d81611af1565b9050919050565b7f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60008201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000602082015250565b6000611b90603d83611562565b9150611b9b82611b34565b604082019050919050565b60006020820190508181036000830152611bbf81611b83565b9050919050565b7f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560008201527f72206f7220617070726f76656400000000000000000000000000000000000000602082015250565b6000611c22602d83611562565b9150611c2d82611bc6565b604082019050919050565b60006020820190508181036000830152611c5181611c15565b9050919050565b7f4552433732313a20696e76616c696420746f6b656e2049440000000000000000600082015250565b6000611c8e601883611562565b9150611c9982611c58565b602082019050919050565b60006020820190508181036000830152611cbd81611c81565b9050919050565b7f4552433732313a2061646472657373207a65726f206973206e6f74206120766160008201527f6c6964206f776e65720000000000000000000000000000000000000000000000602082015250565b6000611d20602983611562565b9150611d2b82611cc4565b604082019050919050565b60006020820190508181036000830152611d4f81611d13565b9050919050565b600081905092915050565b6000611d6c82611557565b611d768185611d56565b9350611d86818560208601611573565b80840191505092915050565b6000611d9e8285611d61565b9150611daa8284611d61565b91508190509392505050565b7f4552433732313a207472616e736665722066726f6d20696e636f72726563742060008201527f6f776e6572000000000000000000000000000000000000000000000000000000602082015250565b6000611e12602583611562565b9150611e1d82611db6565b604082019050919050565b60006020820190508181036000830152611e4181611e05565b9050919050565b7f4552433732313a207472616e7366657220746f20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000611ea4602483611562565b9150611eaf82611e48565b604082019050919050565b60006020820190508181036000830152611ed381611e97565b9050919050565b7f4552433732313a20617070726f766520746f2063616c6c657200000000000000600082015250565b6000611f10601983611562565b9150611f1b82611eda565b602082019050919050565b60006020820190508181036000830152611f3f81611f03565b9050919050565b7f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560008201527f63656976657220696d706c656d656e7465720000000000000000000000000000602082015250565b6000611fa2603283611562565b9150611fad82611f46565b604082019050919050565b60006020820190508181036000830152611fd181611f95565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600081519050919050565b600082825260208201905092915050565b600061202e82612007565b6120388185612012565b9350612048818560208601611573565b6120518161159d565b840191505092915050565b6000608082019050612071600083018761169e565b61207e602083018661169e565b61208b60408301856117b4565b818103606083015261209d8184612023565b905095945050505050565b6000815190506120b7816114c8565b92915050565b6000602082840312156120d3576120d2611492565b5b60006120e1848285016120a8565b9150509291505056fea26469706673582212202317cb8ebcfd012abe2765f22dc46757bec68ea616c3429fbfb991becdf73a9764736f6c63430008120033"; - + const signer = client.web3.getConnectedSigner(); // the bytecode automatically mints tokenID 0 to the deployer const factory = new ContractFactory( ERC721_ABI, - erc721Bytecode, - client.web3.getConnectedSigner(), + ERC721_ABI_BYTECODE, + signer, ); - return factory.deploy("Test NFT", "TNFT"); + const contract = await factory.deploy("Test NFT", "TNFT", "https://example.org/metadata.json"); + await contract.mint(signer.getAddress()); + return contract; } diff --git a/modules/client/test/integration/client/decoding.test.ts b/modules/client/test/integration/client/decoding.test.ts index 31ca6191d..d4f45e82f 100644 --- a/modules/client/test/integration/client/decoding.test.ts +++ b/modules/client/test/integration/client/decoding.test.ts @@ -174,6 +174,12 @@ describe("Client", () => { } }); it("Should decode an encoded withdraw action of an erc721 token", async () => { + // encoded withdraw erc721 action data + // type: ERC721 + // tokenAddress: 0x000000000000000000000000000000000003 + // tokenId: 10 + // recipientAddressOrEns: 0x000000000000000000000000000000000002 + // daoAddressOrEns: 0x000000000000000000000000000000000001 const data = "0x42842e0e00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a"; const context = new Context(contextParamsLocalChain); @@ -199,6 +205,40 @@ describe("Client", () => { fail("TokenType should be ERC721"); } }); + it("Should decode an encoded withdraw action of an erc1155 token", async () => { + // encoded withdraw erc1155 action data + // type: ERC1155 + // tokenAddress: 0x000000000000000000000000000000000001 + // recipientAddressOrEns: 0x000000000000000000000000000000000002 + // daoAddressOrEns: 0x000000000000000000000000000000000003 + // tokenIds: [10] + // amounts: [20] + const data = + "0xf242432a00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000"; + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + const params = client.decoding.withdrawAction( + ADDRESS_ONE, + BigInt(0), + hexToBytes(data), + ); + if (params.type === TokenType.ERC1155) { + expect(params.type).toBe(TokenType.ERC1155); + expect(params.tokenIds.toString()).toBe([BigInt(10)].toString()); + expect(params.amounts.toString()).toBe([BigInt(20)].toString()); + expect(params.daoAddressOrEns).toBe( + ADDRESS_THREE, + ) + expect(params.recipientAddressOrEns).toBe( + ADDRESS_TWO, + ); + expect(params.tokenAddress).toBe( + ADDRESS_ONE, + ); + } else { + fail("TokenType should be ERC1155"); + } + }); it("Should decode an encoded update metadata action", async () => { const context = new Context(contextParamsLocalChain); diff --git a/modules/client/test/integration/client/encoding.test.ts b/modules/client/test/integration/client/encoding.test.ts index c80304564..fc92088a4 100644 --- a/modules/client/test/integration/client/encoding.test.ts +++ b/modules/client/test/integration/client/encoding.test.ts @@ -111,6 +111,31 @@ describe("Client", () => { "0x42842e0e00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a", ); }); + it("Should create a client and generate an ERC1155 withdraw action", async () => { + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + + const withdrawParams: WithdrawParams = { + type: TokenType.ERC1155, + tokenAddress: ADDRESS_ONE, + recipientAddressOrEns: ADDRESS_TWO, + daoAddressOrEns: ADDRESS_THREE, + tokenIds: [BigInt(10)], + amounts: [BigInt(20)], + }; + + const withdrawAction = await client.encoding.withdrawAction( + withdrawParams, + ); + + expect(typeof withdrawAction).toBe("object"); + expect(withdrawAction.data).toBeInstanceOf(Uint8Array); + expect(withdrawAction.to).toBe(withdrawParams.tokenAddress); + expect(withdrawAction.value).toBe(BigInt(0)); + expect(bytesToHex(withdrawAction.data)).toBe( + "0xf242432a00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000", + ); + }); it("Should create a client and generate a grant action", () => { const context = new Context(contextParamsLocalChain); const client = new Client(context); diff --git a/modules/client/test/integration/client/estimation.test.ts b/modules/client/test/integration/client/estimation.test.ts index 75c09524b..003e057d4 100644 --- a/modules/client/test/integration/client/estimation.test.ts +++ b/modules/client/test/integration/client/estimation.test.ts @@ -15,6 +15,8 @@ import * as deployContracts from "../../helpers/deployContracts"; import { Server } from "ganache"; import { deployErc20 } from "../../helpers/deploy-erc20"; import { Context, TokenType } from "@aragon/sdk-client-common"; +import { deployErc1155 } from "../../helpers/deploy-erc1155"; +import { deployErc721 } from "../../helpers/deploy-erc721"; let daoAddress = "0x1234567890123456789012345678901234567890"; describe("Client", () => { @@ -94,6 +96,67 @@ describe("Client", () => { expect(gasFeesEstimation.max).toBeGreaterThan(BigInt(0)); expect(gasFeesEstimation.max).toBeGreaterThan(gasFeesEstimation.average); }); + it("Should estimate gas fees for making an erc721 deposit", async () => { + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + + const tokenContract = await deployErc721(); + const depositParams: DepositParams = { + type: TokenType.ERC721, + daoAddressOrEns: daoAddress, + tokenAddress: tokenContract.address, + tokenId: BigInt(0), + }; + + const gasFeesEstimation = await client.estimation.deposit(depositParams); + + expect(typeof gasFeesEstimation).toEqual("object"); + expect(typeof gasFeesEstimation.average).toEqual("bigint"); + expect(typeof gasFeesEstimation.max).toEqual("bigint"); + expect(gasFeesEstimation.max).toBeGreaterThan(BigInt(0)); + expect(gasFeesEstimation.max).toBeGreaterThan(gasFeesEstimation.average); + }); + it("Should estimate gas fees for making an erc1155 deposit", async () => { + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + + const tokenContract = await deployErc1155(); + const depositParams: DepositParams = { + type: TokenType.ERC1155, + daoAddressOrEns: daoAddress, + tokenAddress: tokenContract.address, + amounts: [BigInt(1)], + tokenIds: [BigInt(0)], + }; + + const gasFeesEstimation = await client.estimation.deposit(depositParams); + + expect(typeof gasFeesEstimation).toEqual("object"); + expect(typeof gasFeesEstimation.average).toEqual("bigint"); + expect(typeof gasFeesEstimation.max).toEqual("bigint"); + expect(gasFeesEstimation.max).toBeGreaterThan(BigInt(0)); + expect(gasFeesEstimation.max).toBeGreaterThan(gasFeesEstimation.average); + }); + it("Should estimate gas fees for making an erc1155 batch deposit", async () => { + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + const tokenContract = await deployErc1155(); + const depositParams: DepositParams = { + type: TokenType.ERC1155, + daoAddressOrEns: daoAddress, + tokenAddress: tokenContract.address, + amounts: [BigInt(1), BigInt(2)], + tokenIds: [BigInt(0), BigInt(0)], + }; + + const gasFeesEstimation = await client.estimation.deposit(depositParams); + + expect(typeof gasFeesEstimation).toEqual("object"); + expect(typeof gasFeesEstimation.average).toEqual("bigint"); + expect(typeof gasFeesEstimation.max).toEqual("bigint"); + expect(gasFeesEstimation.max).toBeGreaterThan(BigInt(0)); + expect(gasFeesEstimation.max).toBeGreaterThan(gasFeesEstimation.average); + }); it("Should estimate gas fees updating allowance", async () => { const context = new Context(contextParamsLocalChain); diff --git a/modules/client/test/integration/client/methods.test.ts b/modules/client/test/integration/client/methods.test.ts index 02479b1d4..aec2f80d0 100644 --- a/modules/client/test/integration/client/methods.test.ts +++ b/modules/client/test/integration/client/methods.test.ts @@ -73,6 +73,7 @@ import { TokenType, } from "@aragon/sdk-client-common"; import { INSTALLATION_ABI } from "../../../src/multisig/internal/constants"; +import { deployErc1155 } from "../../helpers/deploy-erc1155"; describe("Client", () => { let daoAddress: string; @@ -200,6 +201,101 @@ describe("Client", () => { }); describe("DAO deposit", () => { + it("Should allow to deposit an ERC1155", async () => { + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + + const erc1155Contract = await deployErc1155(); + const tokenId = BigInt(0); + const deployer = await client.web3.getConnectedSigner().getAddress(); + const deployerBalance = await erc1155Contract.balanceOf( + deployer, + tokenId, + ); + expect(deployerBalance.toString()).toBe("10"); + const steps = client.methods.deposit({ + type: TokenType.ERC1155, + daoAddressOrEns: daoAddress, + tokenIds: [tokenId], + amounts: [BigInt(7)], + tokenAddress: erc1155Contract.address, + }); + for await (const step of steps) { + switch (step.key) { + case DaoDepositSteps.DEPOSITING: + expect(step.txHash).toMatch(/^0x[A-Fa-f0-9]{64}$/i); + break; + case DaoDepositSteps.DONE: + expect(step.tokenIds?.length).toBe(1); + expect(step.tokenIds?.[0].toString()).toBe(tokenId.toString()); + expect(step.amounts?.length).toBe(1); + expect(step.amounts?.[0].toString()).toBe(BigInt(7).toString()); + break; + } + } + const daoBalance = await erc1155Contract.balanceOf(daoAddress, tokenId); + expect(daoBalance.toString()).toBe("7"); + }); + it("Should allow to batch deposit an ERC1155", async () => { + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + + const erc1155Contract = await deployErc1155(); + const deployer = await client.web3.getConnectedSigner().getAddress(); + const tokenIds = [BigInt(0), BigInt(1), BigInt(2)]; + const amounts = [BigInt(7), BigInt(8), BigInt(9)]; + const deployerBalanceId0 = await erc1155Contract.balanceOf( + deployer, + tokenIds[0], + ); + const deployerBalanceId1 = await erc1155Contract.balanceOf( + deployer, + tokenIds[1], + ); + const deployerBalanceId2 = await erc1155Contract.balanceOf( + deployer, + tokenIds[2], + ); + expect(deployerBalanceId0.toString()).toBe("10"); + expect(deployerBalanceId1.toString()).toBe("10"); + expect(deployerBalanceId2.toString()).toBe("10"); + const steps = client.methods.deposit({ + type: TokenType.ERC1155, + daoAddressOrEns: daoAddress, + tokenIds, + amounts, + tokenAddress: erc1155Contract.address, + }); + for await (const step of steps) { + switch (step.key) { + case DaoDepositSteps.DEPOSITING: + expect(step.txHash).toMatch(/^0x[A-Fa-f0-9]{64}$/i); + break; + case DaoDepositSteps.DONE: + expect(step.tokenIds?.length).toBe(3); + expect(step.tokenIds?.toString()).toBe(tokenIds.toString()); + expect(step.amounts?.length).toBe(3); + expect(step.amounts?.toString()).toBe(amounts.toString()); + break; + } + } + const daoBalanceId0 = await erc1155Contract.balanceOf( + daoAddress, + tokenIds[0], + ); + const daoBalanceId1 = await erc1155Contract.balanceOf( + daoAddress, + tokenIds[1], + ); + const daoBalanceId2 = await erc1155Contract.balanceOf( + daoAddress, + tokenIds[2], + ); + + expect(daoBalanceId0.toString()).toBe(amounts[0].toString()); + expect(daoBalanceId1.toString()).toBe(amounts[1].toString()); + expect(daoBalanceId2.toString()).toBe(amounts[2].toString()); + }); it("Should allow to deposit an ERC721", async () => { const context = new Context(contextParamsLocalChain); const client = new Client(context); @@ -403,6 +499,45 @@ describe("Client", () => { ).toBe("7"); }); + it("Should allow to deposit native toekn", async () => { + const context = new Context(contextParamsLocalChain); + const client = new Client(context); + + const provider = client.web3.getProvider(); + + const amount = BigInt(7); + + const depositParams: DepositParams = { + type: TokenType.NATIVE, + daoAddressOrEns: daoAddress, + amount, + }; + + // Deposit + for await (const step of client.methods.deposit(depositParams)) { + switch (step.key) { + case DaoDepositSteps.DEPOSITING: + expect(typeof step.txHash).toBe("string"); + expect(step.txHash).toMatch(/^0x[A-Fa-f0-9]{64}$/i); + break; + case DaoDepositSteps.DONE: + expect(typeof step.amount).toBe("bigint"); + expect(step.amount).toBe(BigInt(7)); + break; + default: + throw new Error( + "Unexpected DAO deposit step: " + JSON.stringify(step, null, 2), + ); + } + } + + const daoBalance = await provider.getBalance(daoAddress); + expect( + daoBalance.toString(), + ).toBe(amount.toString()); + + }); + it("Check if dao factory has register dao permission in the dao registry", async () => { const context = new Context(contextParamsLocalChain); const client = new Client(context); diff --git a/modules/client/test/integration/constants.ts b/modules/client/test/integration/constants.ts index 1968991f6..895b75c1f 100644 --- a/modules/client/test/integration/constants.ts +++ b/modules/client/test/integration/constants.ts @@ -9,7 +9,7 @@ import { ContextParams } from "@aragon/sdk-client-common"; const IPFS_API_KEY = process?.env?.IPFS_API_KEY || ""; const SATSUMA_ENDPOINT = - "https://subgraph.satsuma-prod.com/qHR2wGfc5RLi6/aragon/osx-goerli/version/v1.2.1/api"; + "https://subgraph.satsuma-prod.com/qHR2wGfc5RLi6/aragon/osx-goerli/version/v1.3.0/api"; export const web3endpoints = { working: [ diff --git a/modules/common/src/errors.ts b/modules/common/src/errors.ts index 07a3ad588..689b7f558 100644 --- a/modules/common/src/errors.ts +++ b/modules/common/src/errors.ts @@ -268,3 +268,11 @@ export class InvalidGasEstimationFactorError extends SdkError { ); } } +export class InvalidParameter extends SdkError { + constructor(message?: string, cause?: Error) { + super( + "Invalid parameter:" + message, + cause, + ); + } +}