Manage API

If a Manager API’s role is solely focused on managing the cohort's foundational information, membership, and identity to provide snapshot rollup data to the Prover, it does not strictly need to follow the Protocol outlined on this page. Manager APIs may adopt more flexible permission structures, alternative verification methods, or even entirely different protocols depending on the provider. The descriptions below refer to the default Manager API as implemented by the Silicon development team.

  • Required Permissions: Owner of the Cohort NFT item on the Silicon Network.

  • Permission Verification: An HTTP header authorization containing the signature from the Cohort Item owner on keccak256(request.body), valid within N seconds from the timestamp (N = 30 seconds).

Add Members to a Cohort

POST /cohort/{cohortId}/member/add

  • Adds an address to a specific cohort. If the address is already included, only its Weight is updated.

// request
{
  "validUntil": 1718266039,
	"members":{
		"0x0000000000000000000000000000000000000400":1,
		"0x0000000000000000000000000000000000000500":2
	},
	
	"signature": "0xabcsafbasdb..."
}

// response
{
	"statusCode": 200
	"data": affectedRowCount
}

Removing Members from a Cohort:

POST /cohort/{cohortId}/member/remove

  • Removes a specified address from a cohort. If the address does not exist in the cohort, the action is ignored.

// request
{
  "validUntil": 1718266039,
	"members":[
		"0x0000000000000000000000000000000000000400", 
		"0x0000000000000000000000000000000000000500"
	],
	
	"signature": "0xabiofsdnboiqnw..."
}

// response
{
	"statusCode": 200
	"data": affectedRowCount
}

Adding or Removing Identity Mapping in a Cohort

POST /identity/update

  • Maps an actual EOA to an address (Identity) within a specified cohort and submits a signature for the mapping.

  • This function operates independently of the snapshot preparation status.

  • Updating signatures for addresses not currently in the cohort is also supported.

  • If a new signature is received and is valid, it will overwrite any previous signature (e.g., EOA change).

  • To delete a signature, set the EOA to the 0x0 address and submit a signed message. However, to prevent the reuse of a previous signature, it’s recommended to process it as a revocation.3

signature

The signature is signed as follows. This signature is used by the AirdropContract, which verifies it through the code below when collecting an ExtraSignature for airdrop targeting this cohort.

  • Silicon Chain Cohort NFT Verification Code

//import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
//import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

bytes32 hash = keccak256(abi.encode(address(cohortNFTAddress), getChainId(), identityEOA, addressEOA); 
bytes32 signingHash = MessageHashUtils.toEthSignedMessageHash(hash);
require(cohortOwner == ECDSA.recover(signingHash, signature));
  • Each identity signature example

const { defaultAbiCoder, keccak256 } = require('ethers/lib/utils');

(async() => {
    const cohortNFTAddress = "0x0000000000000000000000000000000000001234";
    const chainId = "1414";
    const uniqueKey = "0x000000000000000000000000000000000000dead";
    const target = "0x7777777777777777777777777777777777777777";

    const dataBytes = defaultAbiCoder.encode(
        ['string', 'address', 'uint256', 'address', 'address'],
        ['OpenCohort:Identity', cohortNFTAddress, chainId, uniqueKey, target]
    );
    console.log(`Data Bytes: ${dataBytes}\\n`);

    const dataHash = keccak256(dataBytes);
    console.log(`Data Hash: ${dataHash}\\n`);

    const signingBytes = Buffer.concat([
        Buffer.from('\\x19Ethereum Signed Message:\\n32', 'ascii'),
        Buffer.from(dataHash.replace("0x",""), 'hex')
    ])
    const signingHash = keccak256(signingBytes);
    console.log(`Signing Hash: ${signingHash}`);
})()

Request & Response

// request
{
	"validUntil": 1718266039,
  "signer":"0x12345..Address" // the address that signed the identity map below
  //"timestamp", "owner" etc. can be included but will be ignored if present 
  
	"identities":{
	  // member address included in Cohort: [actual mapped EOA address, signature by Cohort Owner for mapping]
		"0x0000000000000000000000000000000000000400":["0xc6a2ad8cc6e4a7e08fc37cc5954be07d499e7654","0xSIGNATURE_R_S_V"], // V= 0x1b or 0x1c
		"0x0000000000000000000000000000000000000500":["0xAddress","0xSIGNATURE_R_S_V"]
	},
	
	"signature": "0xaoidsghngoiqwng..."
}

// fail response
{
	"statusCode":500
		
	// Request method check
	"message":"invalid_identity",  // if the identity item is missing or not in the Dict<EOA, [EOA, SIG]> format
	"message":"invalid_signature", // if the SIGNATURE provided for any identity item is invalid or not from the signer (in this case, no request processing occurs; validation is prioritized)
}

Preparing a Cohort Snapshot

POST /cohort/{cohortId}/snapshot/initialize

  • Confirms nonce 0 snapshot data for a Cohort with a specified Hash and generates a snapshot.

// request
{
  "validUntil": 1718266039,
  "signature": "0xabsabnoiqwengqwe..."
}

// response
// If the Merkle Root Hash generated from the addresses currently in the cohort matches the Merkle Root Hash set on-chain at nonce 0, it will immediately process the same actions as a snapshot submission.
{
	"statusCode":200
	"message": "success"
}

// fail response
{
	"successCod":500
	"message": error_message	
}

POST /cohort/{cohortId}/snapshot/prepare

  • Requests the necessary information to create a finalized snapshot of the addresses currently under management. This is only possible if the RootHash for the Cohort item is generated.

// request
{
  "validUntil": 1718266039,
  "snapshotTime": 1718266039,
  "signature": "0x1o2hnioadsngioqw..."
}

// response
{
	"data": {
		"signatureInfo": {
		  "address": "0x0000000000000000000000000000000000000700",
			"cohortId":{cohortId},
			"merkleRoot":"0xd0e15e9b115375b9725e57604121792e871db3128dc0c5683601d8288f755930",
			"timestamp":1718266039, // Snapshot reference time (for rollup, returned exactly as requested in the request).
			"prover":"<https://api-prover.silicon.network>",
			"totalWeight":10000000,
			"nonce":1, // The first snapshot begins at 1.
		}
		
		"expiredAt":1718266099 // Submit signatures within this time. 
	}
}

// fail response
{
	"successCod":500
	"message": error_message	
}

Confirmation of Cohort Snapshot

POST /cohort/{cohortId}/snapshot/submit

  • Signatures for a pending snapshot are submitted via prepare, finalizing the snapshot data and storing the rollup data.

Signature

The signature is generated based on the signatureInfo provided in prepare, as shown below.

  • Reference: Silicon Network Cohort NFT verification code

//import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
//import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

bytes32 hash = keccak256(abi.encode("OpenCohort:Rollup", address(cohortNFTAddress), getChainId(), CohortId, nonce, merkleRoot, totalWeight, totalCount, prover, timestamp)); // tokenId == CohortId
bytes32 signingHash = MessageHashUtils.toEthSignedMessageHash(hash);
require(cohortOwner == ECDSA.recover(signingHash, signature));
  • Example of a rollup signature

const { defaultAbiCoder, keccak256 } = require('ethers/lib/utils');

(async() => {
    const cohortNFTAddress = "0x0000000000000000000000000000000000001234";
    const chainId = "1414";
    const tokenId = "1234";
    const nonce = "1";
    const merkleRoot = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
    const totalWeight = "10000000000000000";
    const totalCount = "1000";
    const prover = "<https://prover.prover.prover>";
    const timestamp = "777777777";

    //bytes32 hash = keccak256(abi.encode(address(this), getChainId(), tokenId, nonce, merkleRoot, totalWeight, totalCount, prover, timestamp));
    const dataBytes = defaultAbiCoder.encode(
        ['string', 'address', 'uint256', 'uint256', 'uint256', 'bytes32', 'uint256',  'uint256', 'string', 'uint256'],
        ['OpenCohort:Rollup', cohortNFTAddress, chainId, cohortId, nonce, merkleRoot, totalWeight, totalCount, prover, timestamp]
    );
    console.log(`Data Bytes: ${dataBytes}\\n`);

    const dataHash = keccak256(dataBytes);
    console.log(`Data Hash: ${dataHash}\\n`);

    const signingBytes = Buffer.concat([
        Buffer.from('\\x19Ethereum Signed Message:\\n32', 'ascii'),
        Buffer.from(dataHash.replace("0x",""), 'hex')
    ])
    const signingHash = keccak256(signingBytes);
    console.log(`Signing Hash: ${signingHash}`);
})()

Request & Response

// request
{
	"validUntil": 1718266039
  "snapshotTime": 1718266039,
  "snapshotSignature": "0xasdghioegqoeipwmngoiadsjmg",
  "signature":"0x99502d2cd9ea810a6a70f16374466fc45795d2c06f74e48....be8d1982ab130f9c257c73a1955479de6aeacd132133d3f861b"
}

// response
{
	"statusCode":200
	"message": "success"
}

// fail response
{
	"successCod":500
	"message": error_message	
}

Last updated

Logo

Copyright HIGHDRAW. All Rights Reserved.