ERC2771Context.test.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. const ethSigUtil = require('eth-sig-util');
  2. const Wallet = require('ethereumjs-wallet').default;
  3. const { getDomain, domainType } = require('../helpers/eip712');
  4. const { expectEvent } = require('@openzeppelin/test-helpers');
  5. const { expect } = require('chai');
  6. const ERC2771ContextMock = artifacts.require('ERC2771ContextMock');
  7. const ERC2771Forwarder = artifacts.require('ERC2771Forwarder');
  8. const ContextMockCaller = artifacts.require('ContextMockCaller');
  9. const { shouldBehaveLikeRegularContext } = require('../utils/Context.behavior');
  10. contract('ERC2771Context', function (accounts) {
  11. const [, anotherAccount] = accounts;
  12. const MAX_UINT48 = web3.utils.toBN(1).shln(48).subn(1).toString();
  13. beforeEach(async function () {
  14. this.forwarder = await ERC2771Forwarder.new('ERC2771Forwarder');
  15. this.recipient = await ERC2771ContextMock.new(this.forwarder.address);
  16. this.domain = await getDomain(this.forwarder);
  17. this.types = {
  18. EIP712Domain: domainType(this.domain),
  19. ForwardRequest: [
  20. { name: 'from', type: 'address' },
  21. { name: 'to', type: 'address' },
  22. { name: 'value', type: 'uint256' },
  23. { name: 'gas', type: 'uint256' },
  24. { name: 'nonce', type: 'uint256' },
  25. { name: 'deadline', type: 'uint48' },
  26. { name: 'data', type: 'bytes' },
  27. ],
  28. };
  29. });
  30. it('recognize trusted forwarder', async function () {
  31. expect(await this.recipient.isTrustedForwarder(this.forwarder.address));
  32. });
  33. context('when called directly', function () {
  34. beforeEach(async function () {
  35. this.context = this.recipient; // The Context behavior expects the contract in this.context
  36. this.caller = await ContextMockCaller.new();
  37. });
  38. shouldBehaveLikeRegularContext(...accounts);
  39. });
  40. context('when receiving a relayed call', function () {
  41. beforeEach(async function () {
  42. this.wallet = Wallet.generate();
  43. this.sender = web3.utils.toChecksumAddress(this.wallet.getAddressString());
  44. this.data = {
  45. types: this.types,
  46. domain: this.domain,
  47. primaryType: 'ForwardRequest',
  48. };
  49. });
  50. describe('msgSender', function () {
  51. it('returns the relayed transaction original sender', async function () {
  52. const data = this.recipient.contract.methods.msgSender().encodeABI();
  53. const req = {
  54. from: this.sender,
  55. to: this.recipient.address,
  56. value: '0',
  57. gas: '100000',
  58. nonce: (await this.forwarder.nonces(this.sender)).toString(),
  59. deadline: MAX_UINT48,
  60. data,
  61. };
  62. req.signature = ethSigUtil.signTypedMessage(this.wallet.getPrivateKey(), {
  63. data: { ...this.data, message: req },
  64. });
  65. expect(await this.forwarder.verify(req)).to.equal(true);
  66. const { tx } = await this.forwarder.execute(req);
  67. await expectEvent.inTransaction(tx, ERC2771ContextMock, 'Sender', { sender: this.sender });
  68. });
  69. it('returns the original sender when calldata length is less than 20 bytes (address length)', async function () {
  70. // The forwarder doesn't produce calls with calldata length less than 20 bytes
  71. const recipient = await ERC2771ContextMock.new(anotherAccount);
  72. const { receipt } = await recipient.msgSender({ from: anotherAccount });
  73. await expectEvent(receipt, 'Sender', { sender: anotherAccount });
  74. });
  75. });
  76. describe('msgData', function () {
  77. it('returns the relayed transaction original data', async function () {
  78. const integerValue = '42';
  79. const stringValue = 'OpenZeppelin';
  80. const data = this.recipient.contract.methods.msgData(integerValue, stringValue).encodeABI();
  81. const req = {
  82. from: this.sender,
  83. to: this.recipient.address,
  84. value: '0',
  85. gas: '100000',
  86. nonce: (await this.forwarder.nonces(this.sender)).toString(),
  87. deadline: MAX_UINT48,
  88. data,
  89. };
  90. req.signature = ethSigUtil.signTypedMessage(this.wallet.getPrivateKey(), {
  91. data: { ...this.data, message: req },
  92. });
  93. expect(await this.forwarder.verify(req)).to.equal(true);
  94. const { tx } = await this.forwarder.execute(req);
  95. await expectEvent.inTransaction(tx, ERC2771ContextMock, 'Data', { data, integerValue, stringValue });
  96. });
  97. });
  98. });
  99. });