Order Utils is a JavaScript package used for generating, hashing, and signing AirSwap orders. Check out the source code on GitHub.
The AirSwap Protocol uses Order structs to communicate, verify and ultimately perform atomic swaps between parties. All Orders passed to the contracts must be correctly structured, with the right datatypes, otherwise the smart contracts will not accept them. We created a library to help:
Create Orders
Generate Order expiries
Check an Order is structured correctly
Check a Quote is structured correctly
Check if the swap contract would accept an Order (e.g. checking balances, approvals etc)
You can find the orders.js
library on NPM within @airswap/order-utils or on the AirSwap GitHub​
Types Order
, Quote
, and Party
refer to the structs defined in the Types contract.
Every Order struct has a .signature.validator
field that specifies the address of the Swap contract that the order is intended for. By executing this function, passing the address of the Swap contract you want to work with, all orders you create using getOrder
in the library will have the correct validator field. You can check the current value using _verifyingContract
setVerifyingContract(verifyingContract) {this._verifyingContract = verifyingContract}
Param | Type | Description | Example |
|
| The swap contract address. |
|
Starting from 100, this function merely increments the nonce by 1 and outputs the new number each time it's called. The Swap contract only accepts each nonce from each Ethereum address once, so this function is used to ensure each Order has a unique nonce. If you create orders using the function getOrder
you do not need to use this function - getOrder
executes generateNonce
itself to fetch a new unique nonce for each new order.
generateNonce() {nonce = nonce + 1return nonce.toString()}
Returns a nonce.
Generates a timestamp expiry for exactly n days in the future.
async generateExpiry(days) {return (await getLatestTimestamp()) + SECONDS_IN_DAY * days}
Param | Type | Description | Example |
|
| The number of days in the future. |
|
Returns an expiry timestamp.
Generates a new order
async getOrder({expiry = '0',nonce = this.generateNonce(),signer = defaults.Party,sender = defaults.Party,affiliate = defaults.Party,}) {if (expiry === '0') {expiry = await this.generateExpiry(1)}return lowerCaseAddresses({expiry: String(expiry),nonce: String(nonce),signer: { ...defaults.Party, ...signer },sender: { ...defaults.Party, ...sender },affiliate: { ...defaults.Party, ...affiliate },signature: signatures.getEmptySignature(this._verifyingContract),})}
All parameters are optional, and unprovided parameters default to the values specified in the function defintion above.
Param | Type | Description |
|
| The timestamp of the order expiry. |
|
| Unique identifying number of the order. |
|
| Details of the signer of the order. |
|
| Details of the sender of the order. |
|
| Details of the affiliate of the order. |
Returns an order struct.
To pass in your own Party values, instead of using a default value, the parameters must be laid out correctly as a json object. E.g.
const order = await orders.getOrder({signer: {wallet: ganacheWallet,token: ASTAddress,amount: '400',},sender: {wallet: senderWallet,token: WETHAddress,amount: '2',},})
Checks that the json Quote provided is structured correctly, and is therefore a valid quote.
isValidQuote(quote) {return ('signer' in quote &&'sender' in quote &&'token' in quote['signer'] &&'token' in quote['sender'] &&'amount' in quote['signer'] &&'amount' in quote['sender'] &&'id' in quote['signer'] &&'id' in quote['sender'] &&!('signature' in quote))}
Param | Type | Description |
|
| The quote struct to be checked. |
Returns true
if the quote is valid.
Checks that the json Order provided is structured correctly, and is therefore a valid order.
Param | Type | Description |
|
| The order struct to be checked. |
Returns true
if the order is valid.
const isValidOrder = order => {return ('nonce' in order &&'expiry' in order &&'signer' in order &&'sender' in order &&'affiliate' in order &&'signature' in order &&'wallet' in order['signer'] &&'wallet' in order['sender'] &&'wallet' in order['affiliate'] &&'token' in order['signer'] &&'token' in order['sender'] &&'token' in order['affiliate'] &&'amount' in order['signer'] &&'amount' in order['sender'] &&'amount' in order['affiliate'] &&'id' in order['signer'] &&'id' in order['sender'] &&'id' in order['affiliate'] &&'signatory' in order['signature'] &&'validator' in order['signature'] &&'r' in order['signature'] &&'s' in order['signature'] &&'v' in order['signature'])}
Checks whether the swap contract would accept or reject a specified order. This includes checking wallet balances, approvals, authorizations, signatures, expiry and nonces,
Param | Type | Description |
|
| The order struct to be checked. |
|
| The Ethereum network. e.g. |
Returns an array. Each element is a string specifying an error with the order. A valid order returns an empty array. e.g. ['Signatory not authorised', 'Signer balance is too low']
Orders submitted to the swap contract contain a signature field that usually must be filled with a signature of the order, signed by the order.signer.wallet
. To prevent replay attacks across different contracts, we include a hashed domain
in our signatures, that specifies the details of the swap contract it is intended to be used on. Due to this added complexity, we have created a library of helper signature functions to do all this for you.
The Swap contract accepts 2 types of signatures: those prefixed with the Ethereum signature header and those without. The signature type is specified usign an order.signature.version
of 0x01
(no header) or 0x45
(header). Our library provides signature functions for both of these options.
You can find the signatures.js
library on NPM within @airswap/order-utils or on the AirSwap GitHub​
Types Order
and Party
refer to the structs defined in the Types contract.
The functions below sign all of the fields of an order except the signature field. The functions then return a signature object that is formatted correctly for the swap contract. This signature needs to be combined into the order object before it can then be passed into the swap contract to be executed.
// Get an order object using the `orders.js` libraryconst order = await orders.getOrder({signer: {wallet: ganacheWallet,token: ASTAddress,amount: '400',},sender: {wallet: senderWallet,token: WETHAddress,amount: '2',},})​// Now sign the order.// Notice that the function response is assigned as the order's signatureorder.signature = await signatures.getWeb3Signature(order,ganacheWallet,swapContractAddress,'http://127.0.0.1:8545',)
Uses a web3 provider to sign an order. This means no private key or seed phrase is required to be passed into the function (just the address), as the provider deals with that for you.
This creates a signature with version 0x45.
Param | Type | Description |
|
| The order to be signed. |
|
| The wallet to sign the order. |
|
| The swap contract address. |
|
| The web3 provider to use. |
Returns an order signature.
Uses a private key to sign the order.
This creates a signature with version 0x01.
Param | Type | Description |
|
| The order to be signed. |
|
| The private key to sign with. |
|
| The swap contract address. |
Returns an order signature.
Note: To get your string private key into the correct form use:
const privateKeyBuffer = Buffer.from(privateKeyString, 'hex')
Uses a private key to sign the order.
This creates a signature with version 0x45.
Param | Type | Description |
|
| The order to be signed. |
|
| The private key to sign with. |
|
| The swap contract address. |
Returns an order signature.
Note: To get your string private key into the correct form use:
const privateKeyBuffer = Buffer.from(privateKeyString, 'hex')
Uses a private key to sign the order.
This creates a signature with version 0x01.
Param | Type | Description |
|
| The order to be signed. |
|
| The private key to sign with. |
|
| The swap contract address. |
Returns an order signature.
Note: To get your string private key into the correct form use:
const privateKeyBuffer = Buffer.from(privateKeyString, 'hex')
Returns a signature struct with all values as empty values, except the signature validator which still takes the value of the swap contract address. The Swap contract allows signerAuthorizations
, whereby the order signer no longer has to have signed the order. Instead users can authorize another address to send in orders on their behalf without a signature. This function can be used to create that empty signature field.
Param | Type | Description |
|
| The swap contract address. |
Checks whether a signature is a valid signature of a given order. This includes verifying that the key that signed the order is the same as the one named in the order.
Param | Type | Description |
|
| The order including a signature. |
Returns true
if the signature is valid
When an order is submitted to the Swap contract, a signature of the order is provided with it, which gives security to the signer that the sender of the order cannot modify the order's contents. This signature is constructed by calculating the hash of the order, before signing this hash. Due to the use of sub-objects in an order, there are many different ways of hashing an order - however the smart contract only accepts one. This helper library provides functions to calculate hashes used throughout the protocol.
You can find the hashes.js
library on NPM within @airswap/order-utils or on the AirSwap GitHub​
Types Order
and Party
refer to the structs defined in the Types contract.
Calculates the hash of an order, ignoring any signature fields that are passed to it. This order hash includes the hashing of the domain
of the order (the smart contract it is to be submitted to), to prevent double spend attacks. This function is executed within the signature functions described in Signing Orders, and therefore if you use those signature functions you do not need to calculate the order hash beforehand.
Param | Type | Description |
|
| The order to be hashed. |
|
| The swap contract address. |
Returns the complete hash of an order, including relevant domain information.
Calculates the hash of a Party struct. This function is executed within hashOrder
to hash each Party before hashing the entire order.
Param | Type | Description |
|
| The party to be hashed. |
Returns the party hash.
Calculates the hash of a contract's domain
. The domain is defined by the domain name (SWAP
), the domain version (currently 2
), and the specific swap contract's address. This is used to prevent double spend attacks.hashDomain
is executed within getOrderHash
.
Param | Type | Description |
|
| The swap contract address. |
Returns the domain hash.
Calculates the hash of an order, ignoring any signature fields that are passed to it. Unlike getOrderHash
, this function does not include the domain
of the order, and is merely used to hash the fields of an order. hashOrder
is executed within getOrderHash
.
Param | Type | Description |
|
| The order to be hashed. |
Returns the order hash.