כתיבת כללי מדיניות לעסקאות

בדף הזה מתוארים המאפיינים שמדיניות העסקאות יכולה לבדוק, ומוצגות דוגמאות לכללים של עסקאות ב-Ethereum‏ (ETHEREUM_TX_V2 ו-EIP712_TYPED_DATA_V1), בביטקוין (BITCOIN_TX_V2) וב-Solana‏ (SOLANA_TX_V1).

במאמר הערכת מדיניות הטרנזקציות מוסבר איך המדיניות משולבת, איזו מדיניות נאכפת ולמה טרנזקציה נדחית.

מבנה הכלל

מדיניות עסקה היא הצהרה אחת של Cedar מסוג permit או forbid לגבי הגורם, הפעולה, המשאב וההקשר:

permit (
  principal,
  action == Action::"buy_transaction",
  resource == Resource::"transaction_management"
)
when {
  context.principal_keyring == "institutional-treasury" &&
  context.schema == "ETHEREUM_TX_V2" &&
  context has amount_token_scaled && context.amount_token_scaled <= 1000000000
};

חשוב לזכור:

  • המאפיינים נמצאים ברמה העליונה של ההקשר. צריך לכתוב context.amount_token_scaled, ולא context.transaction.spend_amount_scaled כמו ב-v1alpha1.
  • חלק מהמאפיינים הם אופציונליים. אם מאפיין אופציונלי לא נמצא בהקשר, המדיניות לא תעבור הערכה, ומדיניות שלא עוברת הערכה לא תואמת. כדי לבדוק אם יש מאפיין אופציונלי, משתמשים ב-has לפני שקוראים אותו, או ב-unless { context has ... } בכלל forbid. למידע נוסף, קראו את תרחיש 6 ותרחיש 8.
  • מחרוזות Cedar הן תלויות אותיות רישיות. השוו בין הכתובות בפורמט שמתואר בטבלה של כל רשת.

לגבי חשבון המשתמש, ראו הבקשה של Cedar. במאמר פעולה ומשאב מפורטות הפעולות ש-CreateTransaction מקבל (buy_transaction,‏ sell_transaction ו-internal_transfer_transaction).

מאפייני הקשר

ההקשר של כל טרנזקציה כולל את המאפיינים שפוענחו מהמטען הייעודי (payload) שלה, את המזהים של מפתח החתימה ושל אוסף המפתחות שלו, ואת מאפייני המטא-נתונים (fiat_amount,‏ validity,‏ principal_value1 ו-principal_value2) שהיוזם מגדיר.

מאפייני המטען הייעודי (payload) זהים בכל בלוקצ'יין. בטבלה הבאה מתוארים כל מאפייני ההקשר, ובטבלאות של Ethereum,‏ Bitcoin ו-Solana מתואר איך חישוב רב-משתתפים מאכלס כל מאפיין בבלוקצ'יין הרלוונטי:

מאפיין סוג הארז מוצג תמיד? ערך
schema String כן פורמט המטען הייעודי (payload): ‏"ETHEREUM_TX_V2", ‏"BITCOIN_TX_V2", ‏"SOLANA_TX_V1" או "EIP712_TYPED_DATA_V1". מידע נוסף זמין במאמר שמות של סכימות.
blockchain String כן ‫"ETH", "BTC" או "SOL".
principal_keyring String כן המזהה של אוסף המפתחות שמכיל את מפתח החתימה, לדוגמה "treasury".
principal_key String כן המזהה של מפתח החתימה, לדוגמה "hot-wallet-1".
secondary_key String כן כתובת היעד, מקבל האסימון או ה-NFT, המפעיל או המשתמש המורשה, או הצד השני ב-EIP-712, כפי שפוענחו מהמטען הייעודי (payload).
amount_token_scaled Long לא הסכום שניתן להמרה שהועבר או אושר, ביחידות של 10-9 של הנכס. השדה הזה מושמט כשלא מועבר סכום שניתן להמרה, למשל פעולת ERC-721 או קריאה לחוזה שלא שולחת ETH. מידע נוסף זמין במאמר סכומים.
token_symbol String לא הסמל של הנכס, לדוגמה "ETH",‏ "BTC",‏ "SOL" או "USDC". השדה הזה מושמט אם הנכס לא מזוהה על ידי Multi-party Compute, ובפעולות ERC-721.
contract_address String לא החוזה של אסימון ERC-20 או ERC-721, או החוזה שנקרא (אתריום), החוזה המאמת (EIP-712) או ההנפקה (סולאנה). לא רלוונטי להעברות מקוריות.
token_standard String לא "ERC721" להעברות ואישורים של ERC-721‏ (ETHEREUM_TX_V2). לא נכלל בהעברות מקוריות ובפעולות של אסימונים ניתנים להחלפה.
token_id String לא ‫ERC-721 tokenId של 256 ביט כמחרוזת בבסיס 10 (ETHEREUM_TX_V2). מושמט עבור setApprovalForAll ועבור עסקאות שאינן ERC-721.
from_address String לא הבעלים הנוכחי (from) בקריאות ERC-721 transferFrom ו-safeTransferFrom, בפורמט סיכום ביקורת (checksum) של EIP-55 עם אותיות רישיות וקטנות.
approve_all Bool לא הדגל approved בקריאות ERC-721 setApprovalForAll(operator, approved): true כשמעניקים אישור למפעיל, false כשמבטלים אותו.
deadline Long לא תאריך התפוגה של הודעת EIP712_TYPED_DATA_V1, בשניות מאז ראשית זמן יוניקס (Unix epoch), מהשדה deadline, sigDeadline או validBefore.
eip712 Record לא מטען הייעודי (payload) המובנה של EIP-712: ‏primary_type, ‏domain ו-message, עם מספרים שלמים של 256 ביט כמחרוזות בבסיס 10. ‫EIP712_TYPED_DATA_V1 בלבד.
chain_id Long לא מזהה השרשרת של EVM, לדוגמה 1 ל-Ethereum Mainnet או 11155111 ל-Sepolia. רק Ethereum ו-EIP-712.
max_fee_atomic Long כן הסכום המקסימלי שאפשר לשלם בעסקה כעמלות רשת, ביחידות של 10-9 מהמטבע המקורי של הבלוקצ'יין. ‫0 למשך EIP712_TYPED_DATA_V1.
payload String כן תקציר המטען הייעודי (payload) בקידוד הקסדצימלי, באותיות קטנות.
time_stamp Long כן ה-createTime של העסקה, בשניות מאז ראשית זמן יוניקס (Unix epoch).
key_lists Record מתוך Set<String> כן רשומה שממפה כל מזהה KeyList במכונה ל-Set<String> של כתובות הבלוקצ'יין ב-KeyList הזה, שפוענחו ב-createTime. רשומה ריקה אם למופע אין רשימות מפתחות. קוראים KeyList עם תחביר של סוגריים מרובעים, לדוגמה context.key_lists["trusted-destinations"]. מידע נוסף זמין במאמר רשימות מפתחות בהקשר של Cedar.

‫principal_keyring ו-principal_key הם מזהים, לא שמות מלאים של משאבים. מזהה מפתח הוא ייחודי רק בתוך אוסף המפתחות שלו. כדי להחיל כלל על כל מפתח ב-KeyRing, צריך לסמן את principal_keyring, או את שני המאפיינים כדי לתת שם למפתח אחד:

context.principal_keyring == "treasury" && context.principal_key == "hot-wallet-1"

אם המנתח של Transaction.schema לא יכול לפענח את המטען הייעודי (payload) או להקרין אותו להקשר, לא מתבצעת הערכה של המדיניות והטרנזקציה נדחית עם UNRECOGNIZED_PAYLOAD. למידע נוסף על סיבות לדחייה

שמות הסכימות

המאפיין schema הוא השם של הערך PayloadSchema ב-API של v1beta: "ETHEREUM_TX_V2", "BITCOIN_TX_V2", "SOLANA_TX_V1" או "EIP712_TYPED_DATA_V1". השמות באותיות קטנות שמשמשים בכללים של v1alpha1, כמו "ethereum.transaction.v2", אף פעם לא תואמים ב-v1beta.

כלל בלי סימון של schema או blockchain חל על כל בלוקצ'יין. כדי להגביל כלל לבלוקצ'יין אחד, מסמנים את התיבה schema.

סכומים

‫amount_token_scaled ו-max_fee_atomic משתמשים באותו קנה מידה לכל נכס: יחידה אחת היא 10-9 מהנכס, כך ש-1.0 מכל נכס הוא 1000000000. כדי לקבל את הערך של כלל, מכפילים את הסכום ב-1,000,000,000 (109). בטבלה הבאה מוצגות כמה דוגמאות:

סכום amount_token_scaled
0.1 ETH 100000000
‫1.0 ETH,‏ 1.0 BTC,‏ 1.0 SOL או 1.0 USDC 1000000000
‫0.0025 ביטקוין (250,000 סאטושי) 2500000
‫1.5 SOL‏ (1,500,000,000 lamports) 1500000000
5,000 דולר ארה"ב 5000000000000

בנכסים עם יותר מ-9 ספרות אחרי הנקודה העשרונית, כמו ETH, החלק בסכום שקטן מ-10-9 מושמט. כלל לא יכול לבדוק את זה.

Ethereum

בטבלה הבאה מתוארים מאפייני ההקשר של מטעני הנתונים (payloads) של ETHEREUM_TX_V2 (טרנזקציות מדור קודם בקידוד RLP וטרנזקציות EIP-2718, כולל EIP-1559):

מאפיין ערך
secondary_key בהעברות של ETH ובקריאות לחוזים, הכתובת של to בעסקה. ב-ERC-20 transfer ו-transferFrom, וב-ERC-721 transferFrom ו-safeTransferFrom, מקבל האסימון או ה-NFT. ב-ERC-20 וב-ERC-721 approve, המשלם המאושר. ב-ERC-721 setApprovalForAll, המפעיל. בפורמט סיכום ביקורת (checksum) של EIP-55 עם אותיות רישיות וקטנות.
amount_token_scaled בהעברות של ETH ובהעברות ואישורים של ERC-20, הסכום. בקריאות לחוזים, ה-ETH שנשלח עם הקריאה. אם לא נשלח ETH, השדה הזה מושמט. הערך הזה תמיד מושמט בהעברות ובאישורים של ERC-721, כדי שתקרות ההוצאה של אסימונים ניתנים להמרה לא יתאימו למזהי טוקנים של ה-NFT.
token_symbol ‫"ETH" להעברות ETH ולקריאות לחוזים ששולחים ETH. לטוקנים מסוג ERC-20 שזוהו ב-Ethereum Mainnet,‏ "USDC",‏ "USDT",‏ "DAI" או "WBTC". מושמט לטוקנים אחרים ולפעולות ERC-721.
contract_address בהעברות ובאישורים של ERC-20 ו-ERC-721, החוזה של הטוקן או ה-NFT. בקריאות לחוזים, החוזה שנקרא. בפורמט סיכום ביקורת (checksum) של EIP-55 עם אותיות רישיות וקטנות. לא רלוונטי להעברות ETH.
token_standard ‫"ERC721" להעברות ואישורים של ERC-721. השדה מושמט בהעברות ETH, בפעולות ERC-20 ובקריאות אחרות לחוזים.
token_id ב-ERC-721 transferFrom, safeTransferFrom ו-approve, tokenId של 256 ביט הוא String בבסיס 10, כך שמזהי NFT אף פעם לא חורגים מ-Long של 64 ביט ב-Cedar. מכיוון שמדובר ב-String, צריך להשוות אותו לשוויון עם == או in [...]; < ו-> משווים בין מחרוזות, לא בין מספרים. השדה מושמט ב-setApprovalForAll ובעסקאות שהן לא ERC-721.
from_address ל-ERC-721 transferFrom ו-safeTransferFrom, הבעלים הנוכחי (from), בפורמט סיכום ביקורת (checksum) של EIP-55 עם אותיות רישיות וקטנות.
approve_all ב-ERC-721 setApprovalForAll(operator, approved), הדגל approved כ-Bool: true כשנותנים למפעיל אישור על כל NFT באוסף, false כשמבטלים אותו.
chain_id מזהה השרשרת מהעסקה. אפשר להשמיט את השדה הזה בעסקאות מדור קודם, אז צריך לבדוק אותו באמצעות has.
max_fee_atomic מגבלת הגז × העמלה המקסימלית לכל גז (או מחיר הגז לעסקאות מדור קודם), ב-gwei.

ב-ERC-20 transfer(address, uint256) (0xa9059cbb), שהבורר שלו ייחודי ל-ERC-20, הסכום של אסימון שחישוב רב-משתתפים לא מזהה מותאם כאילו לאסימון היו 18 ספרות אחרי הנקודה העשרונית. התאמה של טוקנים מזוהים לפי contract_address, בלי להגדיר מגבלות סכום על טוקנים לא מזוהים שהמספר העשרוני שלהם הוא לא 18. ‫ERC-20 transferFrom ו-approve חולקים את הסלקטורים שלהם עם ERC-721, ולכן הם צריכים חוזה מוכר. קריאה לחוזה לא מוכר עם אחד מהסלקטורים האלה נדחית עם UNRECOGNIZED_PAYLOAD. מידע נוסף זמין במאמר אסימונים חסרי תחליף (NFT) מסוג ERC-721.

בהקשר לא מצוין איזה סוג של עסקת אתריום זו, אז חשוב לזכור:

  • קריאות לחוזים ששולחות ETH נראות כמו העברות ETH. בשניהם יש token_symbol "ETH" וamount_token_scaled. רק לקריאה לחוזה יש contract_address. כדי לאפשר רק העברות של ETH, מוסיפים את !(context has contract_address).
  • אישורי ERC-20 נראים כמו העברות ERC-20. ל-approve יש את אותם contract_address,‏ amount_token_scaled ו-secondary_key (המוציא) כמו להעברה של אותו סכום לאותה כתובת. מדיניות שמאפשרת את ההעברה מאפשרת גם את האישור.

אי אפשר לפענח טרנזקציות של יצירת חוזים (עם כתובת to ריקה), והן נדחות עם UNRECOGNIZED_PAYLOAD.

טוקנים חסרי-תחליף (NFT) מסוג ERC-721

ב-ETHEREUM_TX_V2, חישוב רב-משתתפים מפענח את השיטות הבאות של ERC-721:

  • safeTransferFrom(address from, address to, uint256 tokenId) (0x42842e0e)
  • safeTransferFrom(address from, address to, uint256 tokenId, bytes data) (0xb88d4fde)
  • transferFrom(address from, address to, uint256 tokenId) (0x23b872dd)
  • approve(address to, uint256 tokenId) (0x095ea7b3)
  • setApprovalForAll(address operator, bool approved) (0xa22cb465)

ל-ERC-20 ול-ERC-721 יש את אותם בוררים של 4 בייטים ל-transferFrom (0x23b872dd) ול-approve (0x095ea7b3). כדי להחליט אם הארגומנט uint256 הוא סכום שניתן להמרה או מזהה טוקן, המערכת של חישוב רב-משתתפים מחפשת את כתובת החוזה to במרשם של חוזי הטוקנים המוכרים. בקריאות ERC-721, המערכת משמיטה את amount_token_scaled, הערך של token_standard הוא "ERC721" והערך של token_id הוא String בבסיס 10. קריאה לחוזה לא מזוהה עם אחד מהבוררים המשותפים האלה נדחית עם UNRECOGNIZED_PAYLOAD.

נתונים מסוג EIP-712

במטענים ייעודיים (payloads) מסוג EIP712_TYPED_DATA_V1 (נתונים מסוג EIP-712 בקידוד UTF-8 JSON עם types,‏ primaryType,‏ domain ו-message), חישוב רב-משתתפים מאמת את מבנה ה-JSON ומחשב את תקציר החתימה של EIP-712 בגודל 32 בייטים, keccak256(0x1901 ++ domainSeparator ++ hashStruct(message)), כאשר ++ הוא שרשור בייטים. האימות דוחה הטמעה עמוקה יותר מ-15 רמות, סוגים מחזוריים, רכיבי מערך של null ושדות של domain שלא תואמים ל-types.EIP712Domain, ושומר על מספרים שלמים של 256 ביט בלי לאבד את הדיוק של הנקודה הצפה.

מטען ייעודי (payload) של EIP712_TYPED_DATA_V1 מקרין את המאפיינים הבאים:

מאפיין ערך
blockchain תמיד "ETH".
schema תמיד "EIP712_TYPED_DATA_V1".
contract_address ‫domain.verifyingContract, בפורמט סיכום ביקורת (checksum) של EIP-55 עם אותיות רישיות וקטנות. השדה מושמט אם הדומיין לא מגדיר את verifyingContract.
chain_id ‫domain.chainId בתור Long. השדה מושמט אם הדומיין לא מגדיר את chainId.
max_fee_atomic תמיד 0, כי חתימה על נתונים שהוקלדו לא כרוכה בתשלום עמלת רשת.
secondary_key בסוגי הודעות מזוהים, הצד השני: spender עבור ERC-2612 Permit ו-Uniswap Permit2, או to עבור ERC-3009 TransferWithAuthorization ו-Safe SafeTx. לסוגי הודעות אחרים, domain.verifyingContract.
amount_token_scaled בסוגי הודעות מזוהים (Permit,‏ Permit2,‏ TransferWithAuthorization ו-SafeTx), ערך האסימון או ה-ETH המורשה (value או details.amount), ביחידות של 10-9. השדה הזה לא מופיע בסוגי הודעות אחרים.
token_symbol ‫"USDC",‏ "USDT",‏ "DAI" או "WBTC" כשהחוזה המאמת או האסימון Permit2‏ (details.token) הוא אסימון מוכר ב-Ethereum Mainnet. אחרת, הוא לא מופיע.
deadline זמן התפוגה של ההודעה, בשניות מאז ראשית זמן יוניקס (Unix epoch), כשההודעה כוללת שדה deadline, sigDeadline או validBefore. אחרת, הוא לא מופיע.
eip712 ‫Record עם הנתונים שהוקלדו. מידע נוסף מופיע בטבלה הבאה.

הרשומה eip712 כוללת את השדות הבאים:

שדה סוג הארז ערך
context.eip712.primary_type String שם ה-primaryType, לדוגמה "Permit",‏ "PermitSingle",‏ "TransferWithAuthorization" או "SafeTx".
context.eip712.domain Record השדות של EIP712Domain שהמטען הייעודי (payload) מגדיר: name,‏ version,‏ chainId,‏ verifyingContract ו-salt.
context.eip712.message Record השדות message. ערכי מספרים שלמים של 256 ביט (uint256 ו-int256) הם String בבסיס 10, כך שהכללים יכולים להתאים לערכים מדויקים בלי גלישה של 64 ביט.

ביטקוין

בטבלה הבאה מתוארים מאפייני ההקשר של המטענים הייעודיים (payloads) של BITCOIN_TX_V2 (BIP 174 PSBT v0):

מאפיין ערך
secondary_key הכתובת של הפלט היחיד שלא משתנה. באיחוד, שבו כל פלט הוא עודף, הכתובת של הפלט הראשון.
amount_token_scaled הסכום שנשלח אל secondary_key, לא כולל עודף (סאטושי אחד = 10). 0 לאיחוד.
token_symbol תמיד "BTC".
contract_address לא מוצג אף פעם.
chain_id אף פעם לא מוצג, כי ב-PSBT לא מצוין לאיזו רשת הוא מיועד. הכתובות תמיד מעוצבות ככתובות Mainnet, כך שגם כתובת Testnet מתחילה ב-bc1, ב-1 או ב-3. התחילית של הכתובת מציינת את סוג הכתובת, לא את הרשת.
max_fee_atomic עמלת הכרייה: סך כל ערכי הקלט פחות סך כל ערכי הפלט (סאטושי אחד = 10).

כדי להפריד בין רשתות ביטקוין, צריך להשתמש במפתחות שונים לכל רשת, למשל ב-KeyRing ל-Mainnet וב-KeyRing אחר ל-Testnet, ולהגדיר להם כללי היקף באמצעות principal_keyring או principal_key. אותו מפתח יכול לחתום בשתי הרשתות, כך שהשיטה הזו פועלת רק אם אף פעם לא משתמשים במפתח Mainnet ב-Testnet.

אי אפשר לפענח עסקה ששולחת ליותר מכתובת אחת שהיא לא כתובת עודף. אי אפשר להעריך עסקה שאין בה כתובת לשליחה, למשל עסקה שהפלט היחיד שלה שאינו עודף הוא פלט OP_RETURN.

Solana

בטבלה הבאה מתוארים מאפייני ההקשר של המטענים הייעודיים (payloads) של SOLANA_TX_V1 (הודעות Solana מדור קודם או בגרסה 0 שלא חתומות):

מאפיין ערך
secondary_key בהעברות SOL, הארנק שאליו מעבירים את הכסף. בהעברות של טוקנים, חשבון הטוקן של היעד, ולא הארנק שבבעלותו. ב-base58.
amount_token_scaled הסכום. ב-SOL, מספר ה-lamports ‏ (lamport אחד = 1). באסימונים, הסכום הגולמי מותאם לפי מספר הספרות אחרי הנקודה העשרונית שמופיע בהוראה TransferChecked.
token_symbol ‫"SOL" להעברות SOL. לאסימונים, "USDC",‏ "USDT" או "PYUSD". מושמט להנפקות אחרות.
contract_address בהעברות של טוקנים, כתובת ההנפקה ב-base58. לא רלוונטי להעברות SOL.
chain_id לא מוצג אף פעם. בהודעות של Solana לא מצוין שם של אשכול.
max_fee_atomic הסכום המקסימלי שאפשר לשלם על העסקה כולה ב-lamport: ‏5,000 לכל חתימה נדרשת, בתוספת עמלת העדיפות, ⌈מגבלת יחידות המחשוב × מחיר יחידת המחשוב במיקרו-lamport ÷ ‏106⌉, אם בעסקה מוגדר מחיר ליחידת מחשוב.

טרנזקציה ב-Solana יכולה להכיל כמה הוראות:

  • הוראות העברה: System Program Transfer (SOL), ו-SPL Token או Token-2022 TransferChecked. אי אפשר לפענח את ההוראה Transfer שלא נבדקה, ואסימונים עם יותר מ-9 ספרות אחרי הנקודה העשרונית.
  • הוראות אחרות: ComputeBudget SetComputeUnitLimit ו-SetComputeUnitPrice, ‏Memo (גרסה 1 וגרסה 2), ‏Associated Token Account Create ו-CreateIdempotent, ו-System Program AdvanceNonceAccount. ההוראות האלה לא יוצרות הקשר משלהן.

כל הוראת העברה נבדקת בנפרד בהקשר שלה. העסקה מותרת רק אם כל העברה מותרת. אם העברה כלשהי נדחית, כל העסקה נדחית. העמלה על כל העסקה היא max_fee_atomic והיא זהה בכל הקשר, אז אל תוסיפו אותה לכל העברה.

אם העסקה מגדירה מחיר ליחידת מחשוב בלי מגבלה על יחידות המחשוב, העמלה משתמשת במגבלת ברירת המחדל של Solana, שהיא 200,000 יחידות מחשוב לכל הוראה, מלבד הוראות ComputeBudget, עד 1,400,000.

החתימות על הטרנזקציה צריכות להתאים לחשבונות שכל הוראה צריכה לחתום עליהם, וחשבונות המקור וחשבון היעד צריכים להיות ניתנים לכתיבה. אי אפשר לפענח הודעות v0 שמשתמשות בטבלאות חיפוש של כתובות. מעבירים את הבייטים של ההודעה הלא חתומה, ולא מעטפת של עסקה חתומה, ב-Transaction.payload וב-Transaction.rawMessage.

ההטבעות המזוהות הן ההטבעות של Solana Mainnet. ב-Devnet או ב-Testnet, להעברת טוקנים אין token_symbol, אז צריך לבדוק את contract_address במקום זאת.

הוראה של Associated Token Account Create מממנת את הרזרבה הפטורה משכירות של החשבון החדש ב-SOL. הסכום הזה הוא לא ב-amount_token_scaled.

כללים לדוגמה

הדוגמאות הבאות הן כללי מדיניות של permit ו-forbid שאפשר להתאים. כל דוגמה בודקת כל מאפיין אופציונלי באמצעות has לפני שהיא קוראת אותו.

מגבלת קנייה של אוסף מפתחות באמצעות מטען ייעודי (payload) ומטא-נתונים

לאשר העברות של buy_transaction בסכום של עד 500,000 USDC ב-Ethereum Mainnet מכל מפתח ב-institutional-treasury KeyRing, כשהיוזם מתייג את העסקה בתווית "equities-desk", מגדיר תוקף של עד 300 שניות ומציין ערך של עד 500,000$:

permit (
  principal,
  action == Action::"buy_transaction",
  resource == Resource::"transaction_management"
)
when {
  context.principal_keyring == "institutional-treasury" &&
  context.schema == "ETHEREUM_TX_V2" &&
  context has chain_id && context.chain_id == 1 &&
  context has contract_address &&
  context.contract_address == "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" &&
  context has amount_token_scaled && context.amount_token_scaled <= 500000000000000 &&
  context has fiat_amount && context.fiat_amount <= 500000 &&
  context has validity && context.validity <= 300 &&
  context has principal_value1 && context.principal_value1 == "equities-desk"
};

חישוב רב-משתתפים לא בודק את fiat_amount מול המטען הייעודי (payload). מידע נוסף מופיע במאמר מאפייני הקשר.

תקרת הוצאות למפתח אחד

לאפשר העברות של עד 1 ETH ממפתח אחד ב-Ethereum Mainnet. ‫!(context has contract_address) לא כולל קריאות לחוזים ששולחות ETH:

permit (principal, action, resource)
when {
  context.principal_keyring == "treasury" &&
  context.principal_key == "hot-wallet-1" &&
  context.schema == "ETHEREUM_TX_V2" &&
  context has chain_id && context.chain_id == 1 &&
  context has token_symbol && context.token_symbol == "ETH" &&
  !(context has contract_address) &&
  context has amount_token_scaled && context.amount_token_scaled <= 1000000000
};

רשימת היתרים של מטבעות דיגיטליים צמודים ב-Ethereum

לאשר העברות של עד 5,000‎ USDC לכתובת אחת, ואישורים (הקצאות) של עד 5,000‎ USDC לכתובת הזו. המדיניות בודקת את contract_address ולא את token_symbol, כך שאסימון מחוזה אחר שמשתמש באותו סמל לא תואם:

permit (principal, action, resource)
when {
  context.schema == "ETHEREUM_TX_V2" &&
  context has contract_address &&
  context.contract_address == "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" &&
  context.secondary_key == "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" &&
  context has amount_token_scaled && context.amount_token_scaled <= 5000000000000
};

הערך של token_symbol הוא "USDC" בעסקה הזו.

העברת ERC-721 ומנגנון הגנה לאישור מפעיל

לאפשר העברה של שטר אוצר אחד שעבר טוקניזציה (token_id == "1042") מחוזה ERC-721 מאושר לכתובת הסדר, ולאסור על מתן אישורים למפעילים ברמת האוסף (setApprovalForAll עם approve_all == true).

מדיניות א' מאפשרת העברה של NFT אחד:

permit (
  principal,
  action == Action::"internal_transfer_transaction",
  resource == Resource::"transaction_management"
)
when {
  context.schema == "ETHEREUM_TX_V2" &&
  context has chain_id && context.chain_id == 1 &&
  context has token_standard && context.token_standard == "ERC721" &&
  context has contract_address &&
  context.contract_address == "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D" &&
  context has token_id && context.token_id == "1042" &&
  context.secondary_key == "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
};

מדיניות ב' אוסרת על אישור מפעילים ברמת האוסף:

forbid (principal, action, resource)
when {
  context.schema == "ETHEREUM_TX_V2" &&
  context has approve_all && context.approve_all == true
};

מגבלת הרשאה ל-USDC ללא גז ב-EIP-712

אישור חתימה על Permit ERC-2612 מחוץ לבלוקצ'יין בסכום של עד 25,000‎ USDC ב-Ethereum Mainnet לנתב סליקה מאושר, כשתוקף האישור פג לפני זמן נתון:

permit (
  principal,
  action == Action::"buy_transaction",
  resource == Resource::"transaction_management"
)
when {
  context.schema == "EIP712_TYPED_DATA_V1" &&
  context has chain_id && context.chain_id == 1 &&
  context has contract_address &&
  context.contract_address == "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" &&
  context has eip712 && context.eip712.primary_type == "Permit" &&
  context.secondary_key == "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45" &&
  context has amount_token_scaled && context.amount_token_scaled <= 25000000000000 &&
  context has deadline && context.deadline <= 1793491200
};

תקרת הוצאות ותקרת עמלות בביטקוין

לאפשר העברות של עד 0.0025 BTC עם עמלת כרייה של עד 10,000 סאטושי, ממפתחות ב-KeyRing שמשמשים רק ב-Bitcoin Mainnet:

permit (principal, action, resource)
when {
  context.schema == "BITCOIN_TX_V2" &&
  context.principal_keyring == "btc-mainnet" &&
  context has amount_token_scaled && context.amount_token_scaled <= 2500000 &&
  context.max_fee_atomic <= 100000
};

מגבלת עמלת עדיפות והעברת SOL

אפשר לבצע העברות של עד 1.5 SOL כשהעמלה הכוללת, כולל עמלת עדיפות, היא עד 50,000 לאמפורטים (0.00005 SOL):

permit (principal, action, resource)
when {
  context.schema == "SOLANA_TX_V1" &&
  context has token_symbol && context.token_symbol == "SOL" &&
  !(context has contract_address) &&
  context has amount_token_scaled && context.amount_token_scaled <= 1500000000 &&
  context.max_fee_atomic <= 50000
};

העברת טוקן SPL או Token-2022 ב-Solana

לאפשר העברות של עד 250‎ USDC לחשבון טוקן אחד. אותו כלל תואם לתוכניות SPL Token ו-Token-2022, כי בהקשר לא מצוין באיזו תוכנית ההעברה משתמשת:

permit (principal, action, resource)
when {
  context.schema == "SOLANA_TX_V1" &&
  context has contract_address &&
  context.contract_address == "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" &&
  context.secondary_key == "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" &&
  context has amount_token_scaled && context.amount_token_scaled <= 250000000000
};

‫secondary_key הוא חשבון הטוקן של המקבל להנפקה הזו. כדי לאפשר ארנק של נמען, צריך לציין את חשבון הטוקן שלו לכל הטבעה שמאפשרים. בעסקה עם כמה העברות, הכלל הזה חייב להתאים לכל העברה.

בלוק נכסים לא מזוהה

דחיית כל העברה שהנכס שלה לא מזוהה על ידי חישוב רב-משתתפים. המדיניות הזו של forbid משתמשת רק ב-has, ולכן היא לא יכולה להיכשל בהערכה:

forbid (principal, action, resource)
when { context has amount_token_scaled }
unless { context has token_symbol };

תקרת העמלה בבלוקצ'יין

‫max_fee_atomic תמיד קיים, ולכן המדיניות של forbid תמיד תעבור הערכה. מכיוון שהקנה מידה הוא 10-9 מכל מטבע מקורי, אותו מספר הוא ערך שונה בכל בלוקצ'יין. מגדירים את היקף המדיניות לבלוקצ'יין אחד:

forbid (principal, action, resource)
when { context.blockchain == "ETH" && context.max_fee_atomic > 10000000 };

ההגדרה הזו דוחה עסקאות ב-Ethereum שבהן אפשר לשלם יותר מ-0.01 ETH בעמלות.

KeyRings

‫KeyRing הוא מאגר סטטי שמקבץ מפתחות MPC, לדוגמה "institutional-treasury" או "btc-cold-storage". כל מפתח נוצר בתוך אוסף מפתחות אחד ונשאר בו באופן קבוע.

אוספי מפתחות משתתפים בכללי מדיניות בשלוש דרכים:

  • KeyRing KeyPolicy (קוורום חתימה). כשיוצרים KeyRing, מגדירים את ה-KeyPolicy הבלתי ניתן לשינוי שלו, שמגדיר את עץ הסף של הצוותים והמשתמשים שמחזיקים במניות מפתח וצריכים לאשר לפני שניתן לחתום על כל מפתח ב-KeyRing. מדיניות העסקאות פועלת לצד KeyPolicy: מדיניות העסקאות מעריכה את הכללים העסקיים של המטען הייעודי (payload) (PERMIT או DENY), ו-KeyPolicy של אוסף המפתחות קובעת אילו חלקים של המפתח צריכים לאשר את טקס החתימה.
  • הגדרת היקף המדיניות של העסקאות באמצעות KeyRing לחתימה (context.principal_keyring). ההקשר של כל עסקה כולל את context.principal_keyring, שמוגדר למזהה של ה-KeyRing שמכיל את מפתח החתימה, ואת context.principal_key, שמוגדר למזהה של מפתח החתימה. כלל עם context.principal_keyring == "institutional-treasury" חל על כל מפתח ב-KeyRing, כולל מפתחות שנוצרו ב-KeyRing הזה מאוחר יותר, בלי ליצור או לאשר מדיניות חדשה:

    permit (
      principal,
      action == Action::"internal_transfer_transaction",
      resource == Resource::"transaction_management"
    )
    when {
      context.principal_keyring == "institutional-treasury" &&
      context has fiat_amount && context.fiat_amount <= 100000
    };
    
  • רשימת היתרים של אוסף מפתחות היעד (ב-KeyList). אפשר גם להוסיף KeyRing כרשומה ב-KeyList, כדי שהעברות לכתובת הבלוקצ'יין של כל מפתח פעיל ב-KeyRing הזה, כמו סריקות פנימיות בין KeyRings חמים וקרים, יאושרו באופן אוטומטי כשנוצרים מפתחות חדשים. לדוגמה, ראו רשימת היתרים ורשימת חסימה של KeyList.

רשימת היתרים ורשימת חסימה של KeyList

אפשר להעביר עד 1 ETH רק לכתובות ב-trusted-destinations KeyList. ‫context.key_lists["trusted-destinations"] היא קבוצת הכתובות ש-KeyList מתרחב אליהן ב-createTime של העסקה, כך שהוספה או הסרה של רשומה משנה את מה שהכלל הזה מאפשר בלי מדיניות חדשה:

// Assuming you created a KeyList with ID "trusted-destinations":
permit (principal, action, resource)
when {
  context.schema == "ETHEREUM_TX_V2" &&
  context has token_symbol && context.token_symbol == "ETH" &&
  !(context has contract_address) &&
  context.key_lists["trusted-destinations"].contains(context.secondary_key) &&
  context has amount_token_scaled && context.amount_token_scaled <= 1000000000
};

דחיית כל עסקה לכתובת ב-KeyList של blocked-addresses, בכל בלוקצ'יין. key_lists ו-secondary_key תמיד קיימים, ו-CreateTransactionPolicy מוודא ש-KeyList קיים, כך שהמדיניות הזו של forbid לא יכולה להיכשל בהערכה:

// Assuming you created a KeyList with ID "blocked-addresses":
forbid (principal, action, resource)
when { context.key_lists["blocked-addresses"].contains(context.secondary_key) };

כותבים את מזהה ה-KeyList בתחביר של סוגריים מרובעים, context.key_lists["..."], במקום בתחביר של נקודה. תחביר הנקודה לא מצליח לנתח מזהים שמכילים מקף או שמתחילים בספרה. הרשומות מושוות כמחרוזות, לכן צריך להוסיף רשומות ידניות בפורמט הכתובת שמתואר בטבלה של כל בלוקצ'יין. למידע נוסף, קראו את המאמר KeyLists בהקשר של Cedar.