Skip to content

Commit

Permalink
add method for creating relayed v2 transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
popenta committed Jan 3, 2024
1 parent ed2bb69 commit abfd4bc
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 4 deletions.
62 changes: 62 additions & 0 deletions src/transactionsFactories/relayedTransactionsFactory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,66 @@ describe("test relayed v1 transaction builder", function () {
"8ede1bbeed96b102344dffeac12c2592c62b7313cdeb132e8c8bf11d2b1d3bb8189d257a6dbcc99e222393d9b9ec77656c349dae97a32e68bdebd636066bf706"
);
});

it("should throw exception when creating relayed v2 transaction with invalid inner transaction", async function () {
let innerTransaction = new TransactionNext({
sender: bob.address.bech32(),
receiver: bob.address.bech32(),
gasLimit: 50000,
chainID: config.chainID,
});

assert.throws(() => {
factory.createRelayedV2Transaction({
innerTransaction: innerTransaction,
innerTransactionGasLimit: 50000,
relayerAddress: carol.address,
}),
"The gas limit should not be set for the inner transaction";
});

innerTransaction.gasLimit = 0;

assert.throws(() => {
factory.createRelayedV2Transaction({
innerTransaction: innerTransaction,
innerTransactionGasLimit: 50000,
relayerAddress: carol.address,
}),
"The inner transaction is not signed";
});
});

it("should create relayed v2 transaction", async function () {
let innerTransaction = new TransactionNext({
sender: bob.address.bech32(),
receiver: "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u",
gasLimit: 0,
chainID: config.chainID,
data: Buffer.from("getContractConfig"),
nonce: 15,
version: 2,
options: 0,
});

const serializedInnerTransaction = transactionComputer.computeBytesForSigning(innerTransaction);
innerTransaction.signature = await bob.signer.sign(Buffer.from(serializedInnerTransaction));

const relayedTransaction = factory.createRelayedV2Transaction({
innerTransaction: innerTransaction,
innerTransactionGasLimit: new BigNumber("60000000"),
relayerAddress: alice.address,
});
relayedTransaction.nonce = 37;

const serializedRelayedTransaction = transactionComputer.computeBytesForSigning(relayedTransaction);
relayedTransaction.signature = await alice.signer.sign(Buffer.from(serializedRelayedTransaction));

assert.equal(relayedTransaction.version, 2);
assert.equal(relayedTransaction.options, 0);
assert.equal(
Buffer.from(relayedTransaction.data).toString(),
"relayedTxV2@000000000000000000010000000000000000000000000000000000000002ffff@0f@676574436f6e7472616374436f6e666967@fc3ed87a51ee659f937c1a1ed11c1ae677e99629fae9cc289461f033e6514d1a8cfad1144ae9c1b70f28554d196bd6ba1604240c1c1dc19c959e96c1c3b62d0c"
);
});
});
52 changes: 48 additions & 4 deletions src/transactionsFactories/relayedTransactionsFactory.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import BigNumber from "bignumber.js";
import { TransactionNext } from "../transaction";
import { IAddress } from "../interface";
import { IAddress, ITransactionNext } from "../interface";
import { ErrInvalidInnerTransaction } from "../errors";
import { Address } from "../address";
import { AddressValue, ArgSerializer, BytesValue, U64Value } from "../smartcontracts";

interface IConfig {
chainID: string;
Expand All @@ -18,7 +19,7 @@ export class RelayedTransactionsFactory {
}

createRelayedV1Transaction(options: {
innerTransaction: TransactionNext;
innerTransaction: ITransactionNext;
relayerAddress: IAddress;
}): TransactionNext {
if (!options.innerTransaction.gasLimit) {
Expand All @@ -32,9 +33,11 @@ export class RelayedTransactionsFactory {
const serializedTransaction = this.prepareInnerTransactionForRelayedV1(options.innerTransaction);
const data = `relayedTx@${Buffer.from(serializedTransaction).toString("hex")}`;

const gasForDataLength = new BigNumber(this.config.gasLimitPerByte).multipliedBy(new BigNumber(data.length));
const additionalGasForDataLength = new BigNumber(this.config.gasLimitPerByte).multipliedBy(
new BigNumber(data.length)
);
const gasLimit = new BigNumber(this.config.minGasLimit)
.plus(gasForDataLength)
.plus(additionalGasForDataLength)
.plus(new BigNumber(options.innerTransaction.gasLimit));

return new TransactionNext({
Expand All @@ -46,6 +49,47 @@ export class RelayedTransactionsFactory {
});
}

createRelayedV2Transaction(options: {
innerTransaction: ITransactionNext;
innerTransactionGasLimit: BigNumber.Value;
relayerAddress: IAddress;
}): TransactionNext {
if (options.innerTransaction.gasLimit) {
throw new ErrInvalidInnerTransaction("The gas limit should not be set for the inner transaction");
}

if (!options.innerTransaction.signature.length) {
throw new ErrInvalidInnerTransaction("The inner transaction is not signed");
}

const { argumentsString } = new ArgSerializer().valuesToString([
new AddressValue(Address.fromBech32(options.innerTransaction.receiver)),
new U64Value(options.innerTransaction.nonce),
new BytesValue(Buffer.from(options.innerTransaction.data)),
new BytesValue(Buffer.from(options.innerTransaction.signature)),
]);

const data = `relayedTxV2@${argumentsString}`;

const additionalGasForDataLength = new BigNumber(this.config.gasLimitPerByte).multipliedBy(
new BigNumber(data.length)
);
const gasLimit = new BigNumber(options.innerTransaction.gasLimit)
.plus(new BigNumber(this.config.minGasLimit))
.plus(additionalGasForDataLength);

return new TransactionNext({
sender: options.relayerAddress.bech32(),
receiver: options.innerTransaction.sender,
value: 0,
gasLimit: gasLimit,
chainID: this.config.chainID,
data: Buffer.from(data),
version: options.innerTransaction.version,
options: options.innerTransaction.options,
});
}

private prepareInnerTransactionForRelayedV1(innerTransaction: TransactionNext): string {
const txObject = {
nonce: new BigNumber(innerTransaction.nonce.toString(), 10).toNumber(),
Expand Down

0 comments on commit abfd4bc

Please sign in to comment.