Smart Contract Integration

Takes a few hours

If rewards are calculated on-chain (like for every core staking contract on Terra), then teams can integrate with the Passport on-chain smart contract.

As shown in the diagram below, teams need to follow the below steps to integrate with both the Passport web2 backend and the Passport on-chain smart contracts:

  • Ask passport team for an api key to be used to get primitives and proof from the Passport web2 backend

  • Issue a web2 API request to receive the scores and Merkle proof: Passport's web2 backend stores a Merkle tree for all of the primitive scores. Rather than storing all wallet scores on-chain (which is transaction heavy and expensive), only the Merkle root will be written on chain. In order to make sure the requested scores' integrity, every on-chain smart contract should pass the scores along with their Merkle proof to the Passport verification smart contract, which will construct a Merkle root from the address + scores + proof. If the Merkle root that it constructed matches the Merkle root on Passport verification contract, then that score is trustworthy.

// Example of requesting scores and proof in Python
headers={'Authorization': 'Api-Key xxxxxxxxxxx'}
url = 'https://<passport_url>/raw_primitives_proof/terra1l8wglwvuz5f8vywzzw8k5eq5qnqy853j0wxq80'
response = requests.get(url, headers=headers)
  • Update your smart contract to accept Passport primitive scores and merkle proofs: A smart contract needs to accept message containing both Passport primitive scores and merkle proofs in order to use the primitive scores in the on-chain computations. Because of the limitation of Cosmwasm message size, the scores will be passed in the schematized form that contains integer and the wallet address only. The details about the schematization is explained below:

# example of a schematized score returned by Passport /raw_primitives_proof/ endpoint
scores = {
	"scores": "{
		\"address\": \"terra10y64wj80kra774c09900qnrcv4yy596q0kptd3\", 
		\"scores\": [
		[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 336396, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 336396, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
		[12], 
		[8965747]
		]
		}", 
	"proof": [
	["-1", "2c67db1bf26dabefdbd94ac8f035942ebb04759615cac66abcdfd057fa05e5a2"], 
	["-1", "5e0f8c493ef7def4cb444d3c7db3e23220c744686d2202a562c4c03162707ee9"], 
	["-1", "01f548ad14be27f61ba2ee9323bdabff217a907a74a8efbebb287e3590f76843"]
	...
	]}

# score schema 
\"scores\": [
	[token_counts], # claimed_token_counts
	[token_counts], # staked_token_counts
	[token_counts], # swapped_in_token_counts
	[token_counts], # swapped_out_token_counts
	[token_counts], # transfer_in_token_counts
	[token_counts], # transfer_out_token_counts
	[token_counts], # unstaked_token_counts
	[token_counts], # voting_token_counts
	[token_counts], # provide_liquidity_counts
	[tx_count], 	# transaction_count
	[wallet_age], 	# wallet_age
]

token_counts = list of the following token's count in order:
ANCHOR_TOKEN, 		# terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76
PYLON_TOKEN, 		# terra1kcthelkax4j9x8d3ny6sdag0qmxxynl3qtcrpy
MIRROR_TOKEN, 		# terra15gwkyepfc6xgca5t5zefzwy42uts8l2m4g40k6
NEXUS_TOKEN, 		# terra12897djskt9rge8dtmm86w654g7kzckkd698608
STARTERRA_TOKEN,        # terra13xujxcrc9dqft4p9a8ls0w3j0xnzm6y2uvve8n
VALKYRIE_TOKEN, 	# terra1dy9kmlm4anr92e42mrkjwzyvfqwz66un00rwr5
ANCHOR_UST_LP_TOKEN, 	# terra1gecs98vcuktyfkrve9czrpgtg0m3aq586x6gzm
NEXUS_UST_LP_TOKEN, 	# terra1q6r8hfdl203htfvpsmyh8x689lp2g0m7856fwd
VALKYRIE_UST_LP_TOKEN,  # terra17fysmcl52xjrs8ldswhz7n6mt37r9cmpcguack
STARTERRA_UST_LP_TOKEN, # terra1uwhf02zuaw7grj6gjs7pxt5vuwm79y87ct5p70
PYLON_UST_LP_TOKEN, 	# terra1rqkyau9hanxtn63mjrdfhpnkpddztv3qav0tq2
MIRROR_UST_LP_TOKEN	# terra17gjf2zehfvnyjtdgua9p9ygquk6gukxe7ucgwh

# example of merkle proofs
proofs = [
	['+1', 'cbd441af056bf79c65a2154bc04ac2e0e40d7a2c0e77b80c27125f47d3d7cba3'],
	['-1', 'db7f4ee8be8025dbffee11b434f179b3b0d0f3a1d7693a441f19653a65662ad3'],
	['-1', 'f235a9eb55315c9a197d069db9c75a01d99da934c5f80f9f175307fb6ac4d8fe'],
	...
]
  • Choice 1) Pass along the Merkle proof and raw primitive scores to the loyalty score smart contract to get a multiplier: The loyalty score smart contract is a middleware that helps to verify the integrity of the scores and returns a composable multiplier from the raw primitive scores. Teams are encouraged to fork the loyalty score smart contract and modify the multiplier formula per their needs. Current available query endpoints are listed below:

pub enum QueryMsg {
    GetAuthenticatedMultiplier { wallet_score: String, proof: Vec<Vec<String>> },
    GetMultiplier { wallet_score: String },
}

// response type of GetAuthenticatedMultiplier
pub struct AuthenticatedMultiplierResponse {
    pub multiplier: cosmwasm_std::Decimal,
}

// response type of GetMultiplier
pub struct MultiplierResponse {
    pub multiplier: cosmwasm_std::Decimal,
}
  • Choice 2) Directly pass along the Merkle proof and primitive scores to the Passport verification smart contract. Current available query endpoints of the verification contract are listed below:

pub enum QueryMsg {
    VerifyScore { scores: String, proof: Vec<Vec<String>> },
}
    
// response type of VerifyScore
pub struct MerkleVerificationResponse {
    pub result: bool,
}
  • Example of querying the contracts

terrad query wasm contract-store terra1ajr226sp93tyd98phmev0mzpptmlzz43me9nap '{"verify_score":{"scores": "{\"address\": \"terra10y64wj80kra774c09900qnrcv4yy596q0kptd3\", \"scores\": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 336396, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 336396, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [12], [8965747]]}", "proof": [["-1", "2c67db1bf26dabefdbd94ac8f035942ebb04759615cac66abcdfd057fa05e5a2"], ["-1", "5e0f8c493ef7def4cb444d3c7db3e23220c744686d2202a562c4c03162707ee9"], ["-1", "01f548ad14be27f61ba2ee9323bdabff217a907a74a8efbebb287e3590f76843"], ["-1", "9dae09bc1491ec0706072798f5c6fc781c92ca9d197b1de6dc6728aa0c7ff17a"], ["-1", "fc6c195c1bfc8d084a18c6bd8ab0e5d56d184dd07b19c502f5c16a0abe4aa35f"], ["-1", "3ee629e477b33efa863a9549b74dad25404b84e13ba111545e2a8b4af28cea7c"], ["-1", "04ce7f53f9e1fde85773b790c97123174a3d9e0642ad6ff196a5172f1e80904e"], ["-1", "71bcbae9a82c409f7fdd03a45a19d0a685307fcd91c39d5e44a2b9334c1351e5"], ["-1", "1873f53a106d2ff7dc5db00fa4a29f06eb021b4633108ad3af1923b317da5558"], ["-1", "278021deceabbecb3be2313ebc75dd9c4a6092aab422f66221f2c9ad7335fc4e"]]}}'
  • Use the multiplier in your reward calculation logic: The protocol's smart contract will need to be updated to make use of the multiplier in their rewards logic

Resources

Terra Testnet loyalty smart contract address: terra1f02cg9v9cul92s2spgwyj704chvp0n2nk6h3jj

Terra Testnet verification contract address: terra1ajr226sp93tyd98phmev0mzpptmlzz43me9nap

Terra Mainnet loyalty smart contract address: terra1xt58s8zwcs3u7zvek9j3ds9gj6trr0p7hzxsvp

Terra Mainnet verification contract address: terra1scmxemz6y47jxt3gdt5s6pysvwfsgggfp58zk5

Last updated