Build
Skip to content

Wallet Configuration

Every Sequence wallet has a configuration defined by a threshold and a list of signers with their corresponding weights.

Configuration layout

NameTypeRangeDescription
Thresholduint161 - 65535Required combined total "weight" of signers for a signature to be considered valid.
Signerssigner[]unlimited - bounded by gasList of signers that with their corresponding "weight"s.

Signer layout

NameTypeRangeDescription
"weight"uint80 - 255"weight" of every signature of the signer.
"address""address"--"address" of the signer, it may be an EOA or another smart contract wallet with EIP-1271 support.

Example

{
  "threshold": 5,
  "signers": [
    {
      "address": "0x4fbf69aa2a75f9942a768dc8da7804ec965f7bea",
      "weight": 2
    },
    {
      "address": "0x596af90cecdbf9a768886e771178fd5561dd27ab",
      "weight": 3
    },
    {
      "address": "0x6192e0fdcd868b3de01c7fbc0ad98baebd7330c1",
      "weight": 2
    },
    {
      "address": "0xec9a7204a43d3f4a82c84fde92d25bfc9110981e",
      "weight": 1
    }
  ]
}

This example has a threshold of 5 and 4 signers.

The valid combinations of signers are:

- 0x4fbf69aa2a75f9942a768dc8da7804ec965f7bea & 0x596af90cecdbf9a768886e771178fd5561dd27ab - combined weight of 2 + 3 = 5
- 0x6192e0fdcd868b3de01c7fbc0ad98baebd7330c1 & 0x596af90cecdbf9a768886e771178fd5561dd27ab - combined weight of 2 + 3 = 5
- 0x4fbf69aa2a75f9942a768dc8da7804ec965f7bea, 0x6192e0fdcd868b3de01c7fbc0ad98baebd7330c1 & 0xec9a7204a43d3f4a82c84fde92d25bfc9110981e - combined weight of 2 + 2 + 1 = 5

Any combination of signers with a combined weight under the threshold is considered invalid; any additional signers above the threshold are ignored.

Configuration hash - ImageHash

The configuration is never stored directly on the contract, but instead is hashed and checked against every time a signature is validated. This allows the wallet contracts to reduce the usage of storage and therefore the gas cost.

Wallets that never have been updated don't store the imageHash directly, instead the imageHash is used as the salt during the contract creation, and signatures are validated against the address of the wallet.

Compute image hash

solidity
  keccak256(abi.encode( uint8 weight_1, address signer_1,
  keccak256(abi.encode( uint8 weight_2, address signer_2,
  keccak256(abi.encode( uint8 weight_3, address signer_3,
  keccak256(abi.encode( uint256 threshold )) )) )) )) 

Initial wallet configuration

The initial wallet configuration determines the address of the wallet, subsequent updates don't change the address.

The wallet address can be computed using the imageHash, the factory and mainModule of the wallet.

Compute wallet address

// The code of the wallet proxy contract
const WalletProxyBytecode =
"0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3";
 
// These values are defined by the wallet context
// they must be known in order to validate the counter-factual wallet imageHash
const factory = "0xf9D09D634Fb818b05149329C1dcCFAeA53639d96";
const mainModule = "0xd01F11855bCcb95f88D7A48492F66410d4637313";
 
// Append the `mainModule` to the `WalletProxyBytecode`
// this completed the creation code of the proxy contract
// used for computing the wallet address as defined by the CREATE2 opcode
const codeHash = ethers.solidityPackedKeccak256(
ethers.solidityPackedKeccak256(
["bytes", "bytes32"],
[WalletContractBytecode, ethers.hexZeroPad(mainModule, 32)]
)
);
 
// Compute the wallet address
const hash = ethers.solidityPackedKeccak256(
ethers.solidityPackedKeccak256(
["bytes1", "address", "bytes32", "bytes32"],
["0xff", factory, salt, codeHash]
)
);
 
const address = ethers.getAddress(ethers.dataSlice(hash, 12));