-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EN-13168: added relayed v1 tx builder #235
Changes from all commits
763e8c0
f0123db
a8940d8
3c67a83
2a45067
36c4c3b
b1b7b25
d0c7766
60673b0
b6e7c99
8efa383
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { loadTestWallets, TestWallet } from "./testutils"; | ||
import { assert} from "chai"; | ||
import * as errors from "./errors"; | ||
import { RelayedTransactionV1Builder } from "./relayedTransactionV1Builder"; | ||
import { Transaction } from "./transaction"; | ||
import { Address } from "./address"; | ||
import { TransactionPayload } from "./transactionPayload"; | ||
|
||
describe("test relayed v1 transaction builder", function () { | ||
let alice: TestWallet, bob: TestWallet; | ||
|
||
before(async function () { | ||
({alice, bob} = await loadTestWallets()); | ||
}); | ||
|
||
it("should throw exception if args were not set", async function () { | ||
const builder = new RelayedTransactionV1Builder(); | ||
assert.throw(() => builder.build(), errors.ErrInvalidRelayedV1BuilderArguments); | ||
|
||
const innerTx = new Transaction({ | ||
nonce: 15, | ||
sender: alice.address, | ||
receiver: Address.fromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"), | ||
gasLimit: 10000000, | ||
chainID: "1", | ||
data: new TransactionPayload("getContractConfig"), | ||
}); | ||
builder.setInnerTransaction(innerTx); | ||
assert.throw(() => builder.build(), errors.ErrInvalidRelayedV1BuilderArguments); | ||
|
||
const networkConfig = { | ||
MinGasLimit: 50_000, | ||
GasPerDataByte: 1_500, | ||
GasPriceModifier: 0.01, | ||
ChainID: "T" | ||
}; | ||
builder.setNetworkConfig(networkConfig); | ||
assert.throw(() => builder.build(), errors.ErrInvalidRelayedV1BuilderArguments); | ||
|
||
builder.setRelayerAddress(alice.getAddress()); | ||
assert.doesNotThrow(() => builder.build()); | ||
}); | ||
|
||
it("should compute relayed v1 transaction", async function () { | ||
const networkConfig = { | ||
MinGasLimit: 50_000, | ||
GasPerDataByte: 1_500, | ||
GasPriceModifier: 0.01, | ||
ChainID: "T" | ||
}; | ||
|
||
const innerTx = new Transaction({ | ||
nonce: 198, | ||
sender: bob.address, | ||
receiver: Address.fromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"), | ||
gasLimit: 60000000, | ||
chainID: networkConfig.ChainID, | ||
data: new TransactionPayload("getContractConfig"), | ||
}); | ||
|
||
await bob.signer.sign(innerTx); | ||
|
||
const builder = new RelayedTransactionV1Builder(); | ||
const relayedTxV1 = builder | ||
.setInnerTransaction(innerTx) | ||
.setRelayerNonce(2627) | ||
.setNetworkConfig(networkConfig) | ||
.setRelayerAddress(alice.address) | ||
.build(); | ||
|
||
await alice.signer.sign(relayedTxV1); | ||
|
||
assert.equal(relayedTxV1.getNonce().valueOf(), 2627); | ||
assert.equal(relayedTxV1.getData().toString(), "relayedTx@7b226e6f6e6365223a3139382c2273656e646572223a2267456e574f65576d6d413063306a6b71764d354241707a61644b46574e534f69417643575163776d4750673d222c227265636569766572223a22414141414141414141414141415141414141414141414141414141414141414141414141414141432f2f383d222c2276616c7565223a302c226761735072696365223a313030303030303030302c226761734c696d6974223a36303030303030302c2264617461223a225a3256305132397564484a68593352446232356d6157633d222c227369676e6174757265223a2239682b6e6742584f5536776674315464437368534d4b3454446a5a32794f74686336564c576e3478724d5a706248427738677a6c6659596d362b766b505258303764634a562b4745635462616a7049692b5a5a5942773d3d222c22636861696e4944223a2256413d3d222c2276657273696f6e223a317d"); | ||
assert.equal(relayedTxV1.getSignature().hex(), "c7d2c3b971f44eca676c10624d3c4319f8898af159f003e1e59f446cb75e5a294c9f0758d800e04d3daff11e67d20c4c1f85fd54aad6deb947ef391e6dd09d07"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { Transaction } from "./transaction"; | ||
import { IAddress, INonce } from "./interface"; | ||
import { INetworkConfig } from "./interfaceOfNetwork"; | ||
import { ErrInvalidRelayedV1BuilderArguments } from "./errors"; | ||
import { TransactionPayload } from "./transactionPayload"; | ||
import { ContractFunction, StringValue } from "./smartcontracts"; | ||
import { Address } from "./address"; | ||
import BigNumber from "bignumber.js"; | ||
|
||
export class RelayedTransactionV1Builder { | ||
innerTransaction: Transaction | undefined; | ||
relayerAddress: IAddress | undefined; | ||
relayerNonce: INonce | undefined; | ||
netConfig: INetworkConfig | undefined; | ||
|
||
/** | ||
* Sets the inner transaction to be used. It has to be already signed. | ||
* | ||
* @param {Transaction} transaction The inner transaction to be used | ||
*/ | ||
setInnerTransaction(transaction: Transaction): RelayedTransactionV1Builder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we have received an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, we cannot use that |
||
this.innerTransaction = transaction; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the network config to be used for building the relayed v1 transaction | ||
* | ||
* @param {INetworkConfig} netConfig The network configuration to be used | ||
*/ | ||
setNetworkConfig(netConfig: INetworkConfig): RelayedTransactionV1Builder { | ||
this.netConfig = netConfig; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the address of the relayer (the one that will actually pay the fee) | ||
* | ||
* @param relayerAddress | ||
*/ | ||
setRelayerAddress(relayerAddress: IAddress): RelayedTransactionV1Builder { | ||
this.relayerAddress = relayerAddress; | ||
return this; | ||
} | ||
|
||
/** | ||
* (optional) Sets the nonce of the relayer | ||
* | ||
* @param relayerNonce | ||
*/ | ||
setRelayerNonce(relayerNonce: INonce) : RelayedTransactionV1Builder { | ||
this.relayerNonce = relayerNonce; | ||
return this; | ||
} | ||
|
||
/** | ||
* Tries to build the relayed v1 transaction based on the previously set fields | ||
* | ||
* @throws ErrInvalidRelayedV1BuilderArguments | ||
* @return Transaction | ||
*/ | ||
build(): Transaction { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here it's perfectly fine that we return the concrete |
||
if (!this.innerTransaction || !this.netConfig || !this.relayerAddress || !this.innerTransaction.getSignature()) { | ||
throw new ErrInvalidRelayedV1BuilderArguments(); | ||
} | ||
|
||
const serializedTransaction = this.prepareInnerTransaction(); | ||
const payload = TransactionPayload.contractCall() | ||
.setFunction(new ContractFunction("relayedTx")) | ||
.setArgs([ | ||
new StringValue(serializedTransaction), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
]) | ||
.build(); | ||
|
||
const gasLimit = this.netConfig.MinGasLimit + this.netConfig.GasPerDataByte * payload.length() + this.innerTransaction.getGasLimit().valueOf(); | ||
let relayedTransaction = new Transaction({ | ||
nonce: this.relayerNonce, | ||
sender: this.relayerAddress, | ||
receiver: this.innerTransaction.getSender(), | ||
value: 0, | ||
gasLimit: gasLimit, | ||
data: payload, | ||
chainID: this.netConfig.ChainID, | ||
}); | ||
|
||
if (this.relayerNonce) { | ||
relayedTransaction.setNonce(this.relayerNonce); | ||
} | ||
|
||
return relayedTransaction; | ||
} | ||
|
||
private prepareInnerTransaction(): string { | ||
if (!this.innerTransaction) { | ||
return ""; | ||
} | ||
|
||
const txObject = { | ||
"nonce": this.innerTransaction.getNonce().valueOf(), | ||
"sender": new Address(this.innerTransaction.getSender().bech32()).pubkey().toString("base64"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I've forgot about this 👍 |
||
"receiver": new Address(this.innerTransaction.getReceiver().bech32()).pubkey().toString("base64"), | ||
"value": new BigNumber(this.innerTransaction.getValue().toString(), 10).toNumber(), | ||
"gasPrice": this.innerTransaction.getGasPrice().valueOf(), | ||
"gasLimit": this.innerTransaction.getGasLimit().valueOf(), | ||
"data": this.innerTransaction.getData().valueOf().toString("base64"), | ||
"signature": Buffer.from(this.innerTransaction.getSignature().hex(), 'hex').toString("base64"), | ||
"chainID": Buffer.from(this.innerTransaction.getChainID().valueOf()).toString("base64"), | ||
"version": this.innerTransaction.getVersion().valueOf(), | ||
}; | ||
|
||
return JSON.stringify(txObject); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,10 +36,18 @@ describe("test relayed v2 transaction builder", function () { | |
chainID: networkConfig.ChainID, | ||
data: new TransactionPayload("getContractConfig"), | ||
}); | ||
builder = builder.setNetworkConfig(networkConfig).setInnerTransactionGasLimit(10).setInnerTransaction(innerTx); | ||
builder = builder | ||
.setNetworkConfig(networkConfig) | ||
.setInnerTransactionGasLimit(10) | ||
.setInnerTransaction(innerTx) | ||
.setRelayerAddress(alice.address); | ||
Comment on lines
+39
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
assert.throw(() => builder.build(), errors.ErrGasLimitShouldBe0ForInnerTransaction); | ||
|
||
innerTx.setGasLimit({ valueOf: function() { return 10; } }); | ||
innerTx.setGasLimit({ | ||
valueOf: function () { | ||
return 10; | ||
} | ||
}); | ||
builder = builder.setNetworkConfig(networkConfig).setInnerTransactionGasLimit(10).setInnerTransaction(innerTx); | ||
assert.throw(() => builder.build(), errors.ErrGasLimitShouldBe0ForInnerTransaction); | ||
}); | ||
|
@@ -67,10 +75,13 @@ describe("test relayed v2 transaction builder", function () { | |
const relayedTxV2 = builder | ||
.setInnerTransaction(innerTx) | ||
.setInnerTransactionGasLimit(60_000_000) | ||
.setRelayerNonce(37) | ||
.setNetworkConfig(networkConfig) | ||
.setRelayerAddress(alice.getAddress()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
.build(); | ||
await alice.signer.sign(relayedTxV2); | ||
|
||
assert.equal(relayedTxV2.getNonce().valueOf(), 37); | ||
assert.equal( | ||
relayedTxV2.getData().toString(), | ||
"relayedTxV2@000000000000000000010000000000000000000000000000000000000002ffff@0f@676574436f6e7472616374436f6e666967@b6c5262d9837853e2201de357c1cc4c9803988a42d7049d26b7785dd0ac2bd4c6a8804b6fd9cf845fe2c2a622774b1a2dbd0a417c9a0bc3f0563a85bd15e710a"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍