UNPKG

@samudai_xyz/web3-sdk

Version:

## All in one web3 integrations for Samudai

637 lines (636 loc) 28.2 kB
import { ethers } from 'ethers'; import { EthSignSignature } from '@gnosis.pm/safe-core-sdk'; import Safe, { EthersAdapter } from '@safe-global/protocol-kit'; import axios from 'axios'; import { Networks } from '../utils/networks'; import SafeApiKit from '@safe-global/api-kit'; import { encodeData, getDecimalsForToken } from '../lib/helpers'; export class Gnosis { //Constructor constructor(provider, chainId) { this.safeAddress = ''; this.provider = null; this.etherAdapter = null; this.txServiceUrl = ''; // private generateBatchTransaction = ( // value: string, // receiverAddresses: string[], // tokenAddress?: string // ): MetaTransactionData[] => { // const transactions: MetaTransactionData[] = [] // const receiverValue: number = parseInt(value) / receiverAddresses.length // if (tokenAddress) { // receiverAddresses.map((receiverAddress) => { // const encodedData = encodeData( // ethers.utils.getAddress(receiverAddress), // receiverValue.toString() // ) // transactions.push({ // to: ethers.utils.getAddress(tokenAddress), // value: '0', // data: encodedData, // operation: 0, // }) // }) // } else { // receiverAddresses.map((receiverAddress) => { // transactions.push({ // to: receiverAddress, // value: receiverValue.toString(), // data: '0x', // operation: 0, // }) // }) // } // return transactions // } this.generateCustomERC20Transaction = (receiverAddress, customERC20Token) => { const transactions = []; customERC20Token.map((token) => { if (token.tokenAddress) { const encodedData = encodeData(ethers.utils.getAddress(receiverAddress), token.value); transactions.push({ to: ethers.utils.getAddress(token.tokenAddress), value: '0', data: encodedData, operation: 0, }); } else { transactions.push({ to: receiverAddress, value: token.value, data: '0x', operation: 0, }); } }); return transactions; }; this.createSingleGnosisTx = async (receiverAddress, value, safeAddress, senderAddress, tokenAddress) => { try { this.safeAddress = ethers.utils.getAddress(safeAddress); if (this.provider) { const safeOwner = await this.provider.getSigner(0); let finalValue = value; this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const safeSDK = await Safe.create({ ethAdapter: this.etherAdapter, safeAddress: this.safeAddress, }); const nonce = await safeService.getNextNonce(this.safeAddress); let encodedCallData = '0x'; if (tokenAddress) { const decimals = await getDecimalsForToken(this.chainId, tokenAddress); finalValue = ethers.utils.parseUnits(value, decimals).toString(); } else { finalValue = ethers.utils.parseEther(value).toString(); } if (tokenAddress) { encodedCallData = encodeData(ethers.utils.getAddress(receiverAddress), finalValue); } const to = tokenAddress ? tokenAddress : receiverAddress; const tokenValue = tokenAddress ? '0' : finalValue; const safeTransactionData = { to: ethers.utils.getAddress(to), data: encodedCallData, value: tokenValue, operation: 0, nonce: nonce, }; const safeTransaction = await safeSDK.createTransaction({ safeTransactionData, }); const safeTxHash = await safeSDK.getTransactionHash(safeTransaction); const senderSignature = await safeSDK.signTransactionHash(safeTxHash); const result = await safeService.proposeTransaction({ safeAddress: this.safeAddress, safeTransactionData: safeTransaction.data, safeTxHash, senderAddress: ethers.utils.getAddress(senderAddress), senderSignature: senderSignature.data, origin: 'Samudai Platform', }); const data = { safeTxHash: safeTxHash, proposedSafeTx: result, }; return data; } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; this.createBatchTx = async (transactions, safeAddress, senderAddress) => { try { this.safeAddress = ethers.utils.getAddress(safeAddress); if (this.provider) { const safeOwner = await this.provider.getSigner(0); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const safeSDK = await Safe.create({ ethAdapter: this.etherAdapter, safeAddress: this.safeAddress, }); const nonce = await safeService.getNextNonce(this.safeAddress); const options = { nonce: nonce, }; const safeTransactionData = []; await Promise.all(transactions.map(async (tx) => { if ((tx === null || tx === void 0 ? void 0 : tx.tokenAddress) === '') { const safeTransactionDataItem = { to: '', data: '', value: '', }; safeTransactionDataItem.to = ethers.utils.getAddress(tx.to); safeTransactionDataItem.data = '0x'; safeTransactionDataItem.value = ethers.utils .parseEther(tx.value) .toString(); safeTransactionData.push(safeTransactionDataItem); } else { const safeTransactionDataItem = { to: '', data: '', value: '', }; const decimals = await getDecimalsForToken(this.chainId, tx === null || tx === void 0 ? void 0 : tx.tokenAddress); const val = ethers.utils .parseUnits(tx.value, decimals) .toString(); safeTransactionDataItem.value = '0'; const encodedCallData = encodeData(ethers.utils.getAddress(tx.to), val); safeTransactionDataItem.to = ethers.utils.getAddress(tx === null || tx === void 0 ? void 0 : tx.tokenAddress); safeTransactionDataItem.data = encodedCallData; safeTransactionData.push(safeTransactionDataItem); } })); const safeTransaction = await safeSDK.createTransaction({ safeTransactionData: safeTransactionData, onlyCalls: true, options, }); const safeTxHash = await safeSDK.getTransactionHash(safeTransaction); const senderSignature = await safeSDK.signTransactionHash(safeTxHash); const result = await safeService.proposeTransaction({ safeAddress: this.safeAddress, safeTransactionData: safeTransaction.data, safeTxHash, senderAddress: ethers.utils.getAddress(senderAddress), senderSignature: senderSignature.data, origin: 'Samudai Platform', }); const data = { safeTxHash: safeTxHash, proposedSafeTx: result, }; return data; } else { throw new Error('Provider not found'); } } catch (error) { throw error; } }; this.getPendingTransactions = async (safeAddress) => { try { if (this.provider) { const safeOwner = await this.provider.getSigner(0); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const pendingTxs = await safeService.getPendingTransactions(ethers.utils.getAddress(safeAddress)); return pendingTxs; } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; /** * Function to return all safe transactions executed * @param safeAddress string - The address of the Gnosis Safe * @returns SafeTransactions - The response from the Gnosis Safe */ this.getExecutedTransactions = async (safeAddress) => { try { const res = await axios.get(`${this.txServiceUrl}/api/v1/safes/${safeAddress}/multisig-transactions/?executed=true`); return res.data; } catch (err) { throw err; } }; this.getRecentTransactions = async (safeAddress) => { try { const res = await axios.get(`${this.txServiceUrl}/api/v1/safes/${safeAddress}/all-transactions/?limit=40&executed=false&queued=true&trusted=true`); return res.data; } catch (err) { throw err; } }; this.getTransactionDetails = async (safeTxHash) => { try { if (this.provider) { const safeOwner = await this.provider.getSigner(0); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const tx = await safeService.getTransaction(safeTxHash); const safeOwners = await safeService.getSafeInfo(this.safeAddress); const data = { safeMultisigTransactionResponse: tx, confirmation: safeOwners.threshold, }; return data; } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; this.isTransactionOwner = async (safeAddress) => { try { if (this.provider) { let isOwner = false; const safeOwner = await this.provider.getSigner(0); const userAddress = await safeOwner.getAddress(); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const safeInfo = await safeService.getSafeInfo(safeAddress); safeInfo.owners.find((owner) => { if (owner === userAddress) { isOwner = true; } else { isOwner = false; } }); return isOwner; } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; this.isTransactionExecutable = async (safeTxHash, safeAddress) => { try { if (this.provider) { let isOwner = false; let isExecutable = false; let isConfirmation = false; const safeOwner = await this.provider.getSigner(0); const userAddress = await safeOwner.getAddress(); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const transaction = await safeService.getTransaction(safeTxHash); const safeInfo = await safeService.getSafeInfo(safeAddress); const isSafeOwner = safeInfo.owners.find((owner) => owner === ethers.utils.getAddress(userAddress)); if (isSafeOwner) { isOwner = true; } else { isOwner = false; } if (isOwner) { const confirmation = transaction.confirmations.find((confirmation) => confirmation.owner === ethers.utils.getAddress(userAddress)); if (confirmation) { isConfirmation = false; } else { isConfirmation = true; } if (transaction.confirmations) { if (transaction.confirmations.length >= safeInfo.threshold) { isExecutable = true; } else { isExecutable = false; } } return { isOwner, isConfirmation, isExecutable, }; } else { return { isOwner, }; } } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; this.confirmTransaction = async (safeTxHash, safeAddress) => { try { if (this.provider) { this.safeAddress = ethers.utils.getAddress(safeAddress); const safeOwner = await this.provider.getSigner(0); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const safeSDK = await Safe.create({ ethAdapter: this.etherAdapter, safeAddress: this.safeAddress, }); const signature = await safeSDK.signTransactionHash(safeTxHash); const result = await safeService.confirmTransaction(safeTxHash, signature.data); return result; } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; this.rejectTransaction = async (safeAddress, nonce, senderAddress) => { try { if (this.provider) { this.safeAddress = ethers.utils.getAddress(safeAddress); const safeOwner = await this.provider.getSigner(0); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const safeSDK = await Safe.create({ ethAdapter: this.etherAdapter, safeAddress: this.safeAddress, }); const safeTransaction = await safeSDK.createRejectionTransaction(nonce); const safeTxHash = await safeSDK.getTransactionHash(safeTransaction); const senderSignature = await safeSDK.signTransactionHash(safeTxHash); const result = await safeService.proposeTransaction({ safeAddress: this.safeAddress, safeTransactionData: safeTransaction.data, safeTxHash, senderAddress: ethers.utils.getAddress(senderAddress), senderSignature: senderSignature.data, origin: 'Samudai Platform', }); const data = { safeTxHash: safeTxHash, proposedSafeTx: result, }; return data; } else { throw new Error('Provider not found'); } } catch (error) { throw error; } }; this.executeTransaction = async (safeTxHash, safeAddress) => { try { if (this.provider) { this.safeAddress = ethers.utils.getAddress(safeAddress); const safeOwner = await this.provider.getSigner(0); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const safeSDK = await Safe.create({ ethAdapter: this.etherAdapter, safeAddress: this.safeAddress, }); const transaction = await safeService.getTransaction(safeTxHash); const safeTransactionData = { to: transaction.to, value: transaction.value, data: transaction.data || '0x', operation: transaction.operation, safeTxGas: transaction.safeTxGas.toString(), baseGas: transaction.baseGas.toString(), gasPrice: parseInt(transaction.gasPrice).toString(), gasToken: transaction.gasToken, refundReceiver: transaction.refundReceiver, nonce: transaction.nonce, }; const safeTransaction = await safeSDK.createTransaction({ safeTransactionData, }); transaction.confirmations.forEach((confirmation) => { const signature = new EthSignSignature(confirmation.owner, confirmation.signature); safeTransaction.addSignature(signature); }); const executeTxResponse = await safeSDK.executeTransaction(safeTransaction); const receipt = executeTxResponse.transactionResponse && (await executeTxResponse.transactionResponse.wait()); return receipt; } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; this.getSafeOwners = async (safeAddress) => { try { const owners = []; const result = await axios.get(`${this.txServiceUrl}/api/v1/safes/${safeAddress}/`); const safeOwners = result.data.owners; for (const owner of safeOwners) { //const address = (await this.provider?.lookupAddress(owner)) || owner owners.push(owner); } return owners; } catch (err) { return null; } }; this.connectGnosis = async (userAddress) => { try { const UserSafes = []; const result = await axios.get(`${this.txServiceUrl}/api/v1/owners/${userAddress}/safes/`); const userSafes = result.data.safes; for (const userSafe of userSafes) { const owners = await this.getSafeOwners(userSafe); UserSafes.push({ safeAddress: userSafe, owners: owners, }); } return UserSafes; } catch (err) { throw err; } }; // Reminder to change the api url before pushing to dev this.getSafeBalance = async (safeAddress) => { try { const result = await axios.get(`${this.txServiceUrl}/api/v1/safes/${safeAddress}/balances/usd/?trusted=false&exclude_spam=false` // `https://safe-transaction-goerli.safe.global/api/v1/safes/0x6744fC3A5A9CAAeC22c939Bb0737679b768C5e4c/balances/usd/?trusted=false&exclude_spam=false` ); const balance = result.data; return balance; } catch (err) { throw err; } }; // Reminder to change the api url before pushing to dev this.getSafeBalanceinUSD = async (safeAddress) => { try { const result = await axios.get(`https://api.portals.fi/v2/account?owner=${safeAddress}&networks=ethereum`); const balance = result.data; return balance; } catch (err) { throw err; } }; this.verifySafe = async (safeAddress) => { try { const result = await axios.get(`${this.txServiceUrl}/api/v1/safes/${safeAddress}/`); if (result.status === 200) { return true; } else { return false; } } catch (err) { throw err; } }; this.customERC20Transfer = async (safeAddress, receiverAddress, senderAddress, customERC20Token) => { try { this.safeAddress = ethers.utils.getAddress(safeAddress); if (this.provider) { const safeOwner = await this.provider.getSigner(0); this.etherAdapter = new EthersAdapter({ ethers: ethers, signerOrProvider: safeOwner, }); const safeService = new SafeApiKit({ txServiceUrl: this.txServiceUrl, ethAdapter: this.etherAdapter, }); const safeSDK = await Safe.create({ ethAdapter: this.etherAdapter, safeAddress: this.safeAddress, }); const nonce = await safeService.getNextNonce(this.safeAddress); const safeTransactionData = this.generateCustomERC20Transaction(receiverAddress, customERC20Token); const options = { nonce, // Optional }; const safeTransaction = await safeSDK.createTransaction({ safeTransactionData, options, }); const safeTxHash = await safeSDK.getTransactionHash(safeTransaction); const senderSignature = await safeSDK.signTransactionHash(safeTxHash); const result = await safeService.proposeTransaction({ safeAddress: this.safeAddress, safeTransactionData: safeTransaction.data, safeTxHash, senderAddress: ethers.utils.getAddress(senderAddress), senderSignature: senderSignature.data, origin: 'Samudai Platform', }); const data = { safeTxHash: safeTxHash, proposedSafeTx: result, }; return data; } else { throw new Error('Provider not found'); } } catch (err) { throw err; } }; this.provider = provider; this.chainId = chainId; Networks.forEach((network) => { if (network.chainId === chainId) { this.txServiceUrl = network.url; } }); } }