Last active
September 26, 2024 16:05
-
-
Save a2468834/5fa6b5698c0b478296fbedfdc12edb0a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; | |
;; Example verifying signature smart contract in FunC | |
;; | |
;; | |
;; Storage layout | |
;; | |
;; uint256 public key: Signer's public key | |
;; uint32 nonce: A monotonic increasing uint counter starting from zero | |
;; | |
;; | |
;; Constants | |
;; | |
const int PREFIX = 0xffff; | |
const int PERMIT_TYPEHASH = "Permit(Address owner,Address spender,uint32 value,uint32 nonce,uint32 deadline)Address(int8 workchainId,uint256 accountId)"H; | |
const int ADDRESS_TYPEHASH = "Address(int8 workchainId,uint256 accountId)"H; | |
const int TEP181DOMAIN_TYPEHASH = "TEP181Domain(uint32 name,uint32 version,int8 workchainId,uint256 verifierContract)"H; | |
;; | |
;; Error codes | |
;; | |
int op::error_invalid_nonce() asm "0x1354 PUSHINT"; | |
int op::error_signature_too_old() asm "0x435e PUSHINT"; | |
int op::error_invalid_signature() asm "0x8abe PUSHINT"; | |
;; | |
;; `recv_internal` functions | |
;; | |
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure { | |
;; Ignore empty internal message | |
if (in_msg.slice_empty?()) { | |
return (); | |
} | |
;; Load two cells from the incoming intMessage and convert them into slices | |
slice slice_1st = in_msg~load_ref().begin_parse(); | |
slice slice_2nd = in_msg~load_ref().begin_parse(); | |
;; Parse the components from the cells | |
slice signature = slice_1st~load_bits(512); | |
int owner_workchain_id = slice_2nd~load_int(8); | |
int owner_account_id = slice_2nd~load_uint(256); | |
int spender_workchain_id = slice_2nd~load_int(8); | |
int spender_account_id = slice_2nd~load_uint(256); | |
int value = slice_2nd~load_uint(32); | |
int nonce = slice_2nd~load_uint(32); | |
int deadline = slice_2nd~load_uint(32); | |
;; Load known information from smart contract storage | |
slice ds = get_data().begin_parse(); | |
int public_key = ds~load_uint(256); | |
int latest_nonce = ds~load_uint(32); | |
(int workchain_id, int verifier_contract) = parse_std_addr(my_address()); | |
;; Check the validity | |
throw_unless(op::error_invalid_nonce(), nonce == latest_nonce); | |
throw_unless(op::error_signature_too_old(), now() <= deadline); | |
;; Update `nonce` | |
set_data( | |
begin_cell() | |
.store_uint(public_key, 256) | |
.store_uint((latest_nonce + 1), 32) | |
.end_cell() | |
); | |
;; Construct structured data and their hashes from `in_msg` | |
int owner = cell_hash( | |
begin_cell() | |
.store_uint(ADDRESS_TYPEHASH, 256) | |
.store_int(owner_workchain_id, 8) | |
.store_uint(owner_account_id, 256) | |
.end_cell() | |
); | |
int spender = cell_hash( | |
begin_cell() | |
.store_uint(ADDRESS_TYPEHASH, 256) | |
.store_int(spender_workchain_id, 8) | |
.store_uint(spender_account_id, 256) | |
.end_cell() | |
); | |
int struct_hash = cell_hash( | |
begin_cell() | |
.store_uint(PERMIT_TYPEHASH, 256) | |
.store_uint(owner, 256) ;; owner | |
.store_uint(spender, 256) ;; spender | |
.store_uint(value, 32) ;; value | |
.store_uint(nonce, 32) ;; nonce | |
.store_uint(deadline, 32) ;; deadline | |
.end_cell() | |
); | |
;; Construct domain separator hash value from `in_msg` | |
int domain_separator_hash = cell_hash( | |
begin_cell() | |
.store_uint(TEP181DOMAIN_TYPEHASH, 256) ;; | |
.store_uint("Permit Ticket"h, 32) ;; name | |
.store_uint("1"h, 32) ;; version | |
.store_int(workchain_id, 8) ;; workchain_id | |
.store_uint(verifier_contract, 256) ;; verifier_contract_address | |
.end_cell() | |
); | |
;; Check Ed25519 signature | |
throw_unless( | |
op::error_invalid_signature(), | |
check_signature( | |
cell_hash( | |
begin_cell().store_uint(PREFIX, 16).store_uint(domain_separator_hash, 256).store_uint(struct_hash, 256).end_cell() | |
), | |
signature, | |
public_key | |
) | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment