Skip to content

Commit

Permalink
fix viem test flow
Browse files Browse the repository at this point in the history
  • Loading branch information
bjfresh committed Sep 21, 2023
1 parent 948ff96 commit 6a6c249
Show file tree
Hide file tree
Showing 4 changed files with 302 additions and 25 deletions.
141 changes: 137 additions & 4 deletions packages/sdk/src/TokenboundClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import {
hexToNumber,
getAddress,
encodeFunctionData,
// Abi,
parseUnits,
BaseError,
ContractFunctionRevertedError,
Abi,
} from "viem"
import { erc6551AccountAbi, erc6551RegistryAbi, erc1155Abi, erc721Abi } from '../abis'
import { erc6551AccountAbi, erc6551RegistryAbi, erc1155Abi, erc721Abi, erc20Abi } from '../abis'
import {
getAccount,
computeAccount,
Expand All @@ -22,9 +26,10 @@ import {
import {
AbstractEthersSigner,
AbstractEthersTransactionResponse,
SegmentedERC1155Bytecode
SegmentedERC6551Bytecode
} from "./types"
import { chainIdToChain, segmentBytecode } from "./utils"
import { normalize } from "viem/ens"

export const NFTTokenType = {
ERC721: "ERC721",
Expand Down Expand Up @@ -314,9 +319,9 @@ class TokenboundClient {
/**
* Deconstructs the bytecode of a tokenbound account into its constituent parts.
* @param {`0x${string}`} params.accountAddress The address of the tokenbound account.
* @returns a Promise that resolves to a SegmentedERC1155Bytecode object, or null if the account is not deployed
* @returns a Promise that resolves to a SegmentedERC6551Bytecode object, or null if the account is not deployed
*/
public async deconstructBytecode({accountAddress}: BytecodeParams): Promise<SegmentedERC1155Bytecode | null> {
public async deconstructBytecode({accountAddress}: BytecodeParams): Promise<SegmentedERC6551Bytecode | null> {

try {
const rawBytecode = await this.publicClient.getBytecode({address: accountAddress})
Expand Down Expand Up @@ -434,6 +439,13 @@ class TokenboundClient {

try {

// return await this.executeCall({
// account: tbAccountAddress,
// to: tokenContract,
// value: BigInt(0),
// data: transferCallData
// })

if(this.signer) { // Ethers

const preparedNFTTransfer = {
Expand Down Expand Up @@ -466,6 +478,127 @@ class TokenboundClient {

}

/**
* Executes an ETH transfer call on a tokenbound account
* @param {string} params.account The tokenbound account address
* @param {number} params.amount The amount of ETH to transfer, in decimal format (eg. 0.1 ETH = 0.1)
* @param {string} params.recipientAddress The address to which the ETH should be transferred
* @returns a Promise that resolves to the transaction hash of the executed call
*/
public async transferETH(params: ETHTransferParams): Promise<`0x${string}`> {
const {
account: tbAccountAddress,
amount,
recipientAddress
} = params

const weiValue = parseUnits(`${amount}`, 18) // convert ETH to wei
let recipient = getAddress(recipientAddress)

// @BJ todo: debug
// const isENS = recipientAddress.endsWith(".eth")
// if (isENS) {
// recipient = await this.publicClient.getEnsResolver({name: normalize(recipientAddress)})
// if (!recipient) {
// throw new Error('Failed to resolve ENS address');
// }
// }
// console.log('RECIPIENT_ADDRESS', recipient)

try {
return await this.executeCall({
account: tbAccountAddress,
to: recipient,
value: weiValue,
data: '0x'
})

} catch(err) {
console.log(err)
if (err instanceof BaseError) {
const revertError = err.walk(err => err instanceof ContractFunctionRevertedError)
if (revertError instanceof ContractFunctionRevertedError) {
const errorName = revertError.data?.errorName ?? ''
console.log('ERROR NAME', errorName)
console.log('REVERT ERROR DATA', revertError)
// do something with `errorName`
}
}
throw err
}

}

/**
* Executes an ERC-20 transfer call on a tokenbound account
* @param {string} params.account The tokenbound account address
* @param {number} params.amount The amount of ERC-20 to transfer, in decimal format (eg. 0.1 USDC = 0.1)
* @param {string} params.recipientAddress The address to which the ETH should be transferred
* @param {string} params.erc20tokenAddress The address of the ERC-20 token contract
* @param {string} params.erc20tokenDecimals The decimal specification of the ERC-20 token
* @returns a Promise that resolves to the transaction hash of the executed call
*/
public async transferERC20(params: ERC20TransferParams): Promise<`0x${string}`> {
const {
account: tbAccountAddress,
amount,
recipientAddress,
erc20tokenAddress,
erc20tokenDecimals,
} = params

if(erc20tokenDecimals < 0 || erc20tokenDecimals > 18) throw new Error("Decimal value out of range. Should be between 0 and 18.")

const amountBaseUnit = parseUnits(`${amount}`, erc20tokenDecimals)

const recipient = recipientAddress.endsWith(".eth")
? await this.publicClient.getEnsResolver({name: normalize(recipientAddress)})
: recipientAddress

const callData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [recipient, amountBaseUnit],
})

const unencodedTransferERC20ExecuteCall = {
abi: erc6551AccountAbi,
functionName: 'executeCall',
args: [erc20tokenAddress, 0, callData],
}

try {

if(this.signer) { // Ethers

const preparedERC20Transfer = {
to: tbAccountAddress,
value: BigInt(0),
data: encodeFunctionData(unencodedTransferERC20ExecuteCall),
}

return await this.signer.sendTransaction(preparedERC20Transfer).then((tx:AbstractEthersTransactionResponse) => tx.hash) as `0x${string}`

}
else if(this.walletClient) {
const { request } = await this.publicClient.simulateContract({
address: getAddress(tbAccountAddress),
account: this.walletClient.account,
...unencodedTransferERC20ExecuteCall
})

return await this.walletClient.writeContract(request)
}
else {
throw new Error("No wallet client or signer available.")
}

} catch (error) {
console.log(error)
throw error
}
}

}

export {
Expand Down
Loading

0 comments on commit 6a6c249

Please sign in to comment.