safe-core-sdk icon indicating copy to clipboard operation
safe-core-sdk copied to clipboard

relay-kit not working in server side nodejs backend

Open francelwebdev opened this issue 1 year ago • 6 comments
trafficstars

relay-kit not working in server side nodejs backend

francelwebdev avatar Nov 27 '23 18:11 francelwebdev

I'm encountering the same issue as described, where the relay-kit is not functioning correctly in a server-side Node.js environment. Like you, I'm also connected to the Sepolia network.

Here's the code I'm using:

const { ethers } = require('ethers');
/* ---------------------------------- SAFE ---------------------------------- */
const Safe = require('@safe-global/protocol-kit').default
const SafeApiKit = require("@safe-global/api-kit").default
const { EthersAdapter, SafeFactory } = require("@safe-global/protocol-kit")

const {
    OperationType,
    MetaTransactionData
} = require('@safe-global/safe-core-sdk-types');
/* --------------------------------- Gelato --------------------------------- */
const { GelatoRelayPack } = require('@safe-global/relay-kit'`

const createRelayTransaction = async ({ senderPrivateKey, receiverAddress, amount, safeAddress }) => {
    try{
        const sender = new ethers.Wallet(senderPrivateKey, provider);

        const senderAddress = await sender.getAddress()

        const value = ethers.utils.parseUnits(amount , 'ether').toString()

        const transactions = [{
            to: receiverAddress,
            data: '0x',
            value
        }]
        console.log({ transactions })

        const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: sender });
        const protocolKit = await Safe.create({ ethAdapter, safeAddress });
        const relayKit = new GelatoRelayPack({ protocolKit });

        const safeTransaction = await relayKit.createRelayedTransaction({ transactions });

        console.log({safeTransaction})

        // const senderAddress = await sender.getAddress()
        const safeTxHash = await protocolKit.getTransactionHash(safeTransaction)
        // Sign transaction to verify that the transaction is coming from owner 1
        const senderSignature = await protocolKit.signTransactionHash(safeTxHash)

        console.log('Safe tx hash: ',safeTxHash)
        console.log('senderSignature: ',senderSignature)

        console.log({
            safeAddress,
            safeTransactionData: safeTransaction.data,
            safeTxHash,
            senderAddress,
            senderSignature: senderSignature.data,
        })

        await safeService.proposeTransaction({
            safeAddress,
            safeTransactionData: safeTransaction.data,
            safeTxHash,
            senderAddress,
            senderSignature: senderSignature.data,
        })

        console.log("Proposal made")

        return {
            safeTxHash,
            safeTransaction: safeTransaction.data
        }
    }catch(err){
        console.error(err)
        throw new Error(err)
    }
}```

Error:
```TypeError: Cannot read properties of undefined (reading 'to')
    at standardizeSafeTransactionData (/Users/gabe/Documents/HC/HC-Crypto-Server/node_modules/@safe-global/protocol-kit/dist/src/utils/transactions/utils.js:24:16)
    at Safe.createTransaction (/Users/gabe/Documents/HC/HC-Crypto-Server/node_modules/@safe-global/protocol-kit/dist/src/Safe.js:250:90)
    at GelatoRelayPack.createTransactionWithHandlePayment (/Users/gabe/Documents/HC/HC-Crypto-Server/node_modules/@safe-global/relay-kit/dist/src/packs/gelato/GelatoRelayPack.js:107:65)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async createRelayTransaction (/Users/gabe/Documents/HC/HC-Crypto-Server/modules/safe_vault.js:358:33)
/Users/gabe/Documents/HC/HC-Crypto-Server/modules/safe_vault.js:394
        throw new Error(err)```

Gabriel-Mbugua avatar Feb 01 '24 16:02 Gabriel-Mbugua

I figured it out. Seems like the issue was the version. Seems like anything past version 1.3.0 won't work.

Gabriel-Mbugua avatar Feb 05 '24 19:02 Gabriel-Mbugua

Hi @Gabriel-Mbugua what did you do in the end? Did it finally work or did you find a solution?

francelwebdev avatar Feb 05 '24 19:02 francelwebdev

Hi @francelwebdev, below is my integration and package versions.

@safe-global/[email protected] @safe-global/[email protected] @safe-global/[email protected] @safe-global/[email protected] @safe-global/[email protected]

Ignore the ERC20 flow. Currently broken. Trying to figure it out.

const createRelayTransaction = async ({ 
    senderPrivateKey, 
    receiverAddress, 
    amount, 
    safeAddress, 
    network, 
    token, 
    sandbox = SANDBOX 
}) => {
    try{    
        console.log(`Creating relay transaction...`)
        const { provider, serviceUrl } = getNetworkDetails({ network, sandbox })

        const sender = new ethers.Wallet(senderPrivateKey, provider);

        const senderAddress = await sender.getAddress()

        const value = ethers.utils.parseUnits(amount , 'ether').toString()

        let transaction = {
            to: receiverAddress,
            data: '0x',
            value,
            // operation: OperationType.Call
        }

        let options = {
            gasLimit: "80000000",
            isSponsored: false
        }


        if(token){
            console.log(`Transfering ${token}`)
            const { inputData, contractAddress } = erc20InputData({
                token,
                network,
                sandbox,
                amount,
                to: receiverAddress
            })

            transaction.to = contractAddress
            transaction.data = inputData
            transaction.value = '0'
            options.gasToken = contractAddress
        }


        const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: sender });
        const protocolKit = await Safe.create({ ethAdapter, safeAddress });
        const relayKit = new GelatoRelayPack({ protocolKit });

        // const safeTransaction = await relayKit.createRelayedTransaction({ safe: protocolKit, transactions, options});
        const safeTransaction = await relayKit.createRelayedTransaction({ 
            safe: protocolKit, 
            transactions: [transaction], 
            options 
        });

        console.log({safeTransaction})

        // const senderAddress = await sender.getAddress()
        const safeTxHash = await protocolKit.getTransactionHash(safeTransaction)

        // Sign transaction to verify that the transaction is coming from the user
        const senderSignature = await protocolKit.signTransactionHash(safeTxHash)
        console.log('Signed transaction...')
        // const senderSignature = await protocolKit.signHash(safeTxHash)
        
        // Create Safe API Kit instance
        const safeService = new SafeApiKit({
            txServiceUrl: serviceUrl,
            ethAdapter
        });

        await safeService.proposeTransaction({
            safeAddress,
            safeTransactionData: safeTransaction.data,
            // safeTransactionData: confirmTransaction,
            safeTxHash,
            senderAddress,
            senderSignature: senderSignature.data,
        })

        console.log("Proposal made")

        const confirmTransaction = await safeService.confirmTransaction(safeTxHash, senderSignature.data)
        console.log(`Transaction confirmed..`,confirmTransaction)

        return {
            safeTxHash,
            safeTransaction: safeTransaction.data
        }
    }catch(err){
        console.error(err)
        throw new Error(err)
    }
}
    senderPrivateKey, 
    receiverAddress, 
    amount, 
    safeAddress, 
    network, 
    token, 
    sandbox = SANDBOX 
}) => {
    try{    
        console.log(`Creating relay transaction...`)
        const { provider, serviceUrl } = getNetworkDetails({ network, sandbox })

        const sender = new ethers.Wallet(senderPrivateKey, provider);

        const senderAddress = await sender.getAddress()

        const value = ethers.utils.parseUnits(amount , 'ether').toString()

        let transaction = {
            to: receiverAddress,
            data: '0x',
            value,
            // operation: OperationType.Call
        }

        let options = { isSponsored: true }


        if(token){
            console.log(`Transfering ${token}`)
            const { inputData, contractAddress } = erc20InputData({
                token,
                network,
                sandbox,
                amount,
                to: receiverAddress
            })

            transaction.to = contractAddress
            transaction.data = inputData
            transaction.value = '0'
            options.gasToken = contractAddress
        }


        const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: sender });
        const protocolKit = await Safe.create({ ethAdapter, safeAddress });
        // const relayKit = new GelatoRelayPack({ apiKey: GELATO_API_KEY, protocolKit });
        const relayKit = new GelatoRelayPack(GELATO_API_KEY);

        // const safeTransaction = await relayKit.createRelayedTransaction({ safe: protocolKit, transactions, options});
        const safeTransaction = await relayKit.createRelayedTransaction({ 
            safe: protocolKit, 
            transactions: [transaction], 
            options 
        });

        console.log({safeTransaction})

        // const senderAddress = await sender.getAddress()
        const safeTxHash = await protocolKit.getTransactionHash(safeTransaction)

        // Sign transaction to verify that the transaction is coming from the user
        const senderSignature = await protocolKit.signTransactionHash(safeTxHash)
        console.log('Signed transaction...')
        // const senderSignature = await protocolKit.signHash(safeTxHash)
        
        // Create Safe API Kit instance
        const safeService = new SafeApiKit({
            txServiceUrl: serviceUrl,
            ethAdapter
        });

        await safeService.proposeTransaction({
            safeAddress,
            safeTransactionData: safeTransaction.data,
            // safeTransactionData: confirmTransaction,
            safeTxHash,
            senderAddress,
            senderSignature: senderSignature.data,
        })

        console.log("Proposal made")

        const confirmTransaction = await safeService.confirmTransaction(safeTxHash, senderSignature.data)
        console.log(`Transaction confirmed..`,confirmTransaction)

        return {
            safeTxHash,
            safeTransaction: safeTransaction.data
        }
    }catch(err){
        console.error(err)
        throw new Error(err)
    }
}```

Gabriel-Mbugua avatar Feb 05 '24 19:02 Gabriel-Mbugua

Hi @francelwebdev, below is my integration and package versions.

@safe-global/[email protected] @safe-global/[email protected] @safe-global/[email protected] @safe-global/[email protected] @safe-global/[email protected]

Hey @Gabriel-Mbugua thank you for adding more information on your issue

Have you tried to explicitly set the following versions:

api-kit: v2.0.0
protocol-kit: v2.0.0
relay-kit: v2.0.0
safe-core-sdk-types: v3.0.0

[email protected] will only work with [email protected]

You should also remove the @safe-global/[email protected]

dasanra avatar Feb 06 '24 10:02 dasanra

Hey @dasanra, thanks for responding. I've made the package changes you suggested above and the signature signing seems to be broken:

const senderSignature = await protocolKit.signTransactionHash(safeTxHash)

Error:

TypeError: __classPrivateFieldGet(...).getBytes is not a function
    at EthersAdapter.signMessage (/Users/gabe/Documents/HC/HC-Crypto-Server/node_modules/@safe-global/protocol-kit/dist/src/adapters/ethers/EthersAdapter.js:159:87)
    at generateSignature (/Users/gabe/Documents/HC/HC-Crypto-Server/node_modules/@safe-global/protocol-kit/dist/src/utils/signatures/utils.js:76:38)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async createRelayTransaction (/Users/gabe/Documents/HC/HC-Crypto-Server/modules/safe_vault.js:548:33)
/Users/gabe/Documents/HC/HC-Crypto-Server/modules/safe_vault.js:578
        throw new Error(err)
              ^

Error: TypeError: __classPrivateFieldGet(...).getBytes is not a function
    at createRelayTransaction (/Users/gabe/Documents/HC/HC-Crypto-Server/modules/safe_vault.js:578:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Gabriel-Mbugua avatar Feb 07 '24 08:02 Gabriel-Mbugua