Skip to content

Commit

Permalink
Feature: Add support for erc1155 deposits and withdrawals (#268)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* Update modules/client/test/integration/client/decoding.test.ts

Co-authored-by: Michael Heuer <[email protected]>

* add separate native transfer function

* fix rebase

* fix satsuma version

* update subgraph endpoint version

---------

Co-authored-by: Michael Heuer <[email protected]>
  • Loading branch information
josemarinas and heueristik authored Aug 17, 2023
1 parent 865d7dd commit fcff2b7
Show file tree
Hide file tree
Showing 28 changed files with 799 additions and 95 deletions.
1 change: 1 addition & 0 deletions modules/client-common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export enum TokenType {
NATIVE = "native",
ERC20 = "erc20",
ERC721 = "erc721",
ERC1155 = "erc1155",
}

/**
Expand Down
1 change: 1 addition & 0 deletions modules/client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 74 additions & 0 deletions modules/client/examples/01-client/05-deposit-erc1155.ts
Original file line number Diff line number Diff line change
@@ -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]
}
```
*/

46 changes: 45 additions & 1 deletion modules/client/examples/05-encoders-decoders/06-withdraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
}
*/
16 changes: 16 additions & 0 deletions modules/client/src/internal/client/decoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 {
Expand All @@ -163,6 +178,7 @@ export class ClientDecoding extends ClientCore implements IClientDecoding {
value,
result,
abiObject.tokenStandard,
abiObject.batch,
);
} catch (e) {
continue;
Expand Down
48 changes: 48 additions & 0 deletions modules/client/src/internal/client/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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");
}
Expand Down
50 changes: 26 additions & 24 deletions modules/client/src/internal/client/estimation.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";
Expand All @@ -29,6 +32,7 @@ import {
PrepareInstallationParams,
TokenType,
} from "@aragon/sdk-client-common";
import { BigNumber } from "@ethersproject/bignumber";

/**
* Estimation module the SDK Generic Client
Expand Down Expand Up @@ -95,32 +99,30 @@ export class ClientEstimation extends ClientCore implements IClientEstimation {
* @return {*} {Promise<GasFeeEstimation>}
* @memberof ClientEstimation
*/
public deposit(
public async deposit(
params: DepositParams,
): Promise<GasFeeEstimation> {
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
*
Expand Down
Loading

0 comments on commit fcff2b7

Please sign in to comment.