Skip to content

Instantly share code, notes, and snippets.

@Theo6890
Last active November 4, 2024 16:12
Show Gist options
  • Save Theo6890/7f05ba4581ad632514e3d57dc992c143 to your computer and use it in GitHub Desktop.
Save Theo6890/7f05ba4581ad632514e3d57dc992c143 to your computer and use it in GitHub Desktop.
EIP712 Solidity & JS
/////////////// Ethers 5.x.x ///////////////
const { ethers } = require('ethers');
/**
* Same data to be signed by both parties to make the exchange
*
* @param {ethers.Wallet} signer - Wallet instance
*
* @param {Object} domain - Domain of `EIP712`
* @param {string} domain.name - Name passed in EIP712 constructor
* @param {string} domain.version - Version passed in EIP712 constructor
* @param {ethers.BigNumber} domain.chaindId - Chain id where the contract is deployed
* @param {string} domain.contractAddr - Address of the contract
*
* @param {Object} exchange - Defined in interface {src/ISwapInternal.sol}
*/
async function signExchange(signer, domain, exchange) {
const types = {
Data: [
{ type: 'address', name: 'nft' },
{ type: 'uint256', name: 'nftId' },
{ type: 'address', name: 'to' },
],
Exchange: [
{ type: 'Data', name: 'partyOne' },
{ type: 'Data', name: 'partyTwo' },
{ type: 'uint256', name: 'deadline' },
{ type: 'uint256', name: 'id' },
],
};
return await signer._signTypedData(domain, types, {
...exchange,
});
}
module.exports = { signExchange };
////////// Signer example //////////
// const wallet = new ethers.Wallet.fromMnemonic(process.env.SEED);
////////// Domain example //////////
/* const domain = {
name: 'Hand 2 Hand Exchange', // name used in Swap contract
version: '1', // version used in Swap contract
chainId: ethers.BigNumber.from('97'),
verifyingContract: '0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990', // Swap contract
}; */
////////// Exchange example //////////
/*
const exchange = {
partyOne: {
nft: '0x326374475908FC640C3DDE59981C721CafF9c828', // WincityPGC
nftId: 1,
to: '0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326',
},
partyTwo: {
nft: '0x326374475908FC640C3DDE59981C721CafF9c828', // WincityPGC
nftId: 20,
to: '0xE94f1fa4F27D9d288FFeA234bB62E1fBC086CA0c',
},
deadline: 1627776000,
id: 234189124 // random id to define an exchange
}
*/
pragma solidity 0.8.23;
import {EIP712} from "openzeppelin-contracts/utils/cryptography/EIP712.sol";
import {ECDSA} from "openzeppelin-contracts/utils/cryptography/ECDSA.sol";
contract Swap is EIP712 {
constructor() EIP712("Hand 2 Hand Exchange", "1") {}
struct Data {
IERC721 nft;
uint256 nftId;
address to;
}
struct Exchange {
// Data for party one - tx initiator
Data partyOne;
// Data for party two - tx validator
Data partyTwo;
uint256 deadline;
uint256 id;
}
function swap(
Exchange calldata exchange,
bytes calldata sigPartyOne,
bytes calldata sigPartyTwo
) external {
Data memory one = exchange.partyOne;
Data memory two = exchange.partyTwo;
// the one setting up `Exchange` data
address initiator = two.to;
// the one validating `Exchange` data and paying gas fee to make the swap
address validator = one.to;
bytes32 digest = _hashTypedDataV4(_hashSwap(exchange));
require(
ECDSA.recover(digest, sigPartyOne) == initiator,
"Tx Initiator Recovery Failed"
);
require(
ECDSA.recover(digest, sigPartyTwo) == validator,
"TX Validator Recovery Failed"
);
...
rest of code logic
...
}
bytes32 internal constant _EXCHANGE_TYPEHASH =
keccak256(
"Exchange(bytes partyOne,bytes partyTwo,uint256 deadline,uint256 id)"
);
function _hashSwap(
Exchange calldata exchange
) internal pure returns (bytes32) {
return keccak256(abi.encode(_EXCHANGE_TYPEHASH, exchange));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment