<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Sila SIPs - Last Call Review</title>
    <description>All SIPs which are in the two-week "last call" status, please help review these and provide your feedback!</description>
    <link>https://sips.sila.org</link>
    <atom:link href="https://sips.sila.org/rss/last-call.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Thu, 08 Oct 2026 11:41:00 +0000</lastBuildDate>
    
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Add chain id to mixed-case checksum address encoding</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #1191 - Add chain id to mixed-case checksum address encoding&lt;/strong&gt; is in Last Call status. It is authored by Juliano Rizzo (@juli) and was originally created 2018-03-18. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://github.com/sila-chain/SIPs/issues/1121&quot;&gt;https://github.com/sila-chain/SIPs/issues/1121&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Simple Summary

This SIP extends [SIP-55](/SIPS/sip-55) by optionally adding a chain id defined by [SIP-155](/SIPS/sip-155) to the checksum calculation.

## Abstract

The [SIP-55](/SIPS/sip-55) was created to prevent users from losing funds by sending them to invalid addresses. This SIP extends [SIP-55](/SIPS/sip-55) to protect users from losing funds by sending them to addresses that are valid but that where obtained from a client of another network.For example, if this SIP is implemented, a wallet can alert the user that is trying to send funds to an Sila Testnet address from an Sila SilaMainnet wallet.  

## Motivation

The motivation of this proposal is to provide a mechanism to allow software to distinguish addresses from different Sila based networks. This proposal is necessary because Sila addresses are hashes of public keys and do not include any metadata. By extending the [SIP-55](/SIPS/sip-55) checksum algorithm it is possible to achieve this objective.

## Specification

Convert the address using the same algorithm defined by [SIP-55](/SIPS/sip-55) but if a registered chain id is provided, add it to the input of the hash function. If the chain id passed to the function belongs to a network that opted for using this checksum variant, prefix the address with the chain id and the `0x` separator before calculating the hash. Then convert the address to hexadecimal, but if the ith digit is a letter (ie. it&apos;s one of `abcdef`) print it in uppercase if the 4*ith bit of the calculated hash is 1 otherwise print it in lowercase.

## Rationale

 Benefits:
 
 - By means of a minimal code change on existing libraries, users are protected from losing funds by mixing addresses of different Sila based networks.

## Implementation

```python
#!/usr/bin/python3
from sha3 import keccak_256
import random
&quot;&quot;&quot;
   addr (str): Hexadecimal address, 40 characters long with 2 characters prefix
   chainid (int): chain id from SIP-155 &quot;&quot;&quot;
def sil_checksum_encode(addr, chainid=1):
    adopted_sip1191 = [30, 31]
    hash_input = str(chainid) + addr.lower() if chainid in adopted_sip1191 else addr[2:].lower()
    hash_output = keccak_256(hash_input.encode(&apos;utf8&apos;)).hexdigest()
    aggregate = zip(addr[2:].lower(),hash_output)
    out = addr[:2] + &apos;&apos;.join([c.upper() if int(a,16) &gt;= 8 else c for c,a in aggregate])
    return out
```

## Test Cases

```python
sil_mainnet = [
&quot;0x27b1fdb04752bbc536007a920d24acb045561c26&quot;,
&quot;0x3599689E6292b81B2d85451025146515070129Bb&quot;,
&quot;0x42712D45473476b98452f434e72461577D686318&quot;,
&quot;0x52908400098527886E0F7030069857D2E4169EE7&quot;,
&quot;0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed&quot;,
&quot;0x6549f4939460DE12611948b3f82b88C3C8975323&quot;,
&quot;0x66f9664f97F2b50F62D13eA064982f936dE76657&quot;,
&quot;0x8617E340B3D01FA5F11F306F4090FD50E238070D&quot;,
&quot;0x88021160C5C792225E4E5452585947470010289D&quot;,
&quot;0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb&quot;,
&quot;0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB&quot;,
&quot;0xde709f2102306220921060314715629080e2fb77&quot;,
&quot;0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359&quot;,
]
rsk_mainnet = [
&quot;0x27b1FdB04752BBc536007A920D24ACB045561c26&quot;,
&quot;0x3599689E6292B81B2D85451025146515070129Bb&quot;,
&quot;0x42712D45473476B98452f434E72461577d686318&quot;,
&quot;0x52908400098527886E0F7030069857D2E4169ee7&quot;,
&quot;0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD&quot;,
&quot;0x6549F4939460DE12611948B3F82B88C3C8975323&quot;,
&quot;0x66F9664f97f2B50F62d13EA064982F936de76657&quot;,
&quot;0x8617E340b3D01Fa5f11f306f4090fd50E238070D&quot;,
&quot;0x88021160c5C792225E4E5452585947470010289d&quot;,
&quot;0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB&quot;,
&quot;0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB&quot;,
&quot;0xDe709F2102306220921060314715629080e2FB77&quot;,
&quot;0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359&quot;,
]
rsk_testnet = [
&quot;0x27B1FdB04752BbC536007a920D24acB045561C26&quot;,
&quot;0x3599689e6292b81b2D85451025146515070129Bb&quot;,
&quot;0x42712D45473476B98452F434E72461577D686318&quot;,
&quot;0x52908400098527886E0F7030069857D2e4169EE7&quot;,
&quot;0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd&quot;,
&quot;0x6549f4939460dE12611948b3f82b88C3c8975323&quot;,
&quot;0x66f9664F97F2b50f62d13eA064982F936DE76657&quot;,
&quot;0x8617e340b3D01fa5F11f306F4090Fd50e238070d&quot;,
&quot;0x88021160c5C792225E4E5452585947470010289d&quot;,
&quot;0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB&quot;,
&quot;0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB&quot;,
&quot;0xDE709F2102306220921060314715629080e2Fb77&quot;,
&quot;0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359&quot;,
]
test_cases = {30 : rsk_mainnet, 31 : rsk_testnet, 1 : sil_mainnet}

for chainid, cases in test_cases.items():
    for addr in cases:
        assert ( addr == sil_checksum_encode(addr,chainid) )
```

## Usage

### Usage  Table

| Network      | Chain id | Supports this SIP |
|-|-|-|
| RSK SilaMainnet  | 30       | Yes               |
| RSK Testnet  | 31       | Yes               |

### Implementation Table

| Project         | SIP Usage        | Implementation |
|-|-|-|
| MyCrypto       | Yes              | [JavaScript](https://github.com/MyCryptoHQ/MyCrypto/blob/develop/common/utils/formatters.ts#L126) |
| MyEtherWallet  | Yes              | [JavaScript](https://github.com/MyEtherWallet/MyEtherWallet/blob/73c4a24f8f67c655749ac990c5b62efd92a2b11a/src/helpers/addressUtils.js#L22) |
| Ledger         | Yes              | [C](https://github.com/LedgerHQ/ledger-app-sil/blob/master/src_common/silUtils.c#L203) |
| Trezor         | Yes              | [Python](https://github.com/trezor/trezor-core/blob/270bf732121d004a4cd1ab129adaccf7346ff1db/src/apps/sila/get_address.py#L32) and [C](https://github.com/trezor/trezor-crypto/blob/4153e662b60a0d83c1be15150f18483a37e9092c/address.c#L62) |
| Web3.js           | Yes              | [JavaScript](https://github.com/sila-chain/web3.js/blob/aaf26c8806bc9fb60cf6dcb6658104963c6c7fc7/packages/web3-utils/src/Utils.js#L140) |
| SilaJS-util   | Yes              | JavaScript |
| ENS address-encoder | Yes | [TypeScript](https://github.com/ensdomains/address-encoder/commit/5bf53b13fa014646ea28c9e5f937361dc9b40590) |

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).


      </description>
        <pubDate>Sun, 18 Mar 2018 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-1191</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-1191</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Atomic Swap-based American Call Option Contract Standard</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #2266 - Atomic Swap-based American Call Option Contract Standard&lt;/strong&gt; is in Last Call status. It is authored by Runchao Han &lt;runchao.han@monash.edu&gt;, Haoyu Lin &lt;chris.haoyul@gmail.com&gt;, Jiangshan Yu &lt;jiangshan.yu@monash.edu&gt; and was originally created 2019-08-17. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://github.com/sila-chain/SIPs/issues/2266&quot;&gt;https://github.com/sila-chain/SIPs/issues/2266&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Simple Summary

A standard for token contracts providing Atomic Swap-based American Call Option functionalities.

## Abstract

This standard provides functionality to make Atomic Swap-based American Call Option payment. The Atomic Swap protocol based on Hashed Time-Locked Contract (HTLC) [^1] has optionality [^2], and such optionality can be utilised to construct American Call Options without trusted third party. This standard defines the common way of implementing this protocol. In particular, this SIP defines technical terms, provides interfaces, and gives reference implementations of this protocol.


## Motivation

Atomic Swap allows users to atomically exchange their tokens without trusted third parties while the HTLC is commonly used for the implementation. However, the HTLC-based Atomic Swap has optionality. More specifically, the swap initiator can choose to proceed or abort the swap for several hours, which gives him time for speculating according to the exchange rate. A discussion[^2] shows that the HTLC-based Atomic Swap is equivalent to an American Call Option in finance. On the other hand,thanks to such optionality, the HTLC-based Atomic Swap can be utilised to construct American Call Options without trusted third party. A paper[^3] proposes a secure Atomic-Swap-based American Call Option protocol on smart contracts. This protocol not only eliminates the arbitrage opportunity but also prevents any party from locking the other party&apos;s money maliciously. This SIP aims at providing the standard of implementing this protocol in existing token standards.

## Specification

The Atomic Swap-based American Call Option smart contract should follow the syntax and semantics of Sila smart contracts.

### Definitions

+ `initiator`: the party who publishes the advertisement of the swap.
+ `participant`: the party who agrees on the advertisement and participates in the swap with `initiator`.
+ `asset`: the amount of token(s) to be exchanged.
+ `premium`: the amount of token(s) that `initiator` pays to `participant` as the premium.
+ `redeem`: the action to claim the token from the other party.
+ `refund`: the action to claim the token from the party herself/himself, because of timelock expiration.
+ `secrect`: a random string chosen by `initiator` as the preimage of a hash.
+ `secrectHash`: a string equals to the hash of `secrect`, used for constructing HTLCs.
+ `timelock`: a timestamp representing the timelimit, before when the asset can be redeemed, and otherwise can only be refunded.

### Storage Variables

#### swap

This mapping stores the metadata of the swap contracts, including the parties and tokens involved. Each contract uses different `secretHash`, and is distinguished by `secretHash`.

```solidity
mapping(bytes32 =&gt; Swap) public swap;
```

#### initiatorAsset

This mapping stores the detail of the asset initiators want to sell, including the amount, the timelock and the state. It is associated with the swap contract with the same `secretHash`.

```solidity
mapping(bytes32 =&gt; InitiatorAsset) public initiatorAsset;
```

#### participantAsset

This mapping stores the details of the asset participants want to sell, including the amount, the timelock and the state. It is associated with the swap contract with the same `secretHash`.

```solidity
mapping(bytes32 =&gt; ParticipantAsset) public participantAsset;
```

#### premiumAsset

This mapping stores the details of the premium initiators attach in the swap contract, including the amount, the timelock and the state. It is associated with the swap contract with the same `secretHash`.

```solidity
mapping(bytes32 =&gt; Premium) public premium;
```


### Methods

#### setup

This function sets up the swap contract, including the both parties involved, the tokens to exchanged, and so on.

```solidity
function setup(bytes32 secretHash, address payable initiator, address tokenA, address tokenB, uint256 initiatorAssetAmount, address payable participant, uint256 participantAssetAmount, uint256 premiumAmount) public payable
```

#### initiate

The initiator invokes this function to fill and lock the token she/he wants to sell and join the contract.

```solidity
function initiate(bytes32 secretHash, uint256 assetRefundTime) public payable
```

#### fillPremium

The initiator invokes this function to fill and lock the premium.

```solidity
function fillPremium(bytes32 secretHash, uint256 premiumRefundTime) public payable
```

#### participate

The participant invokes this function to fill and lock the token she/he wants to sell and join the contract.

```solidity
function participate(bytes32 secretHash, uint256 assetRefundTime) public payable
```

#### redeemAsset

One of the parties invokes this function to get the token from the other party, by providing the preimage of the hash lock `secret`.

```solidity
function redeemAsset(bytes32 secret, bytes32 secretHash) public
```

#### refundAsset

One of the parties invokes this function to get the token back after the timelock expires.

```solidity
function refundAsset(bytes32 secretHash) public
```

#### redeemPremium

The participant invokes this function to get the premium. This can be invoked only if the participant has already invoked `participate` and the participant&apos;s token is redeemed or refunded.

```solidity
function redeemPremium(bytes32 secretHash) public
```

#### refundPremium

The initiator invokes this function to get the premium back after the timelock expires.

```solidity
function refundPremium(bytes32 secretHash) public
```


### Events

#### SetUp

This event indicates that one party has set up the contract using the function `setup()`.

```solidity
event SetUp(bytes32 secretHash, address initiator, address participant, address tokenA, address tokenB, uint256 initiatorAssetAmount, uint256 participantAssetAmount, uint256 premiumAmount);
```

#### Initiated

This event indicates that `initiator` has filled and locked the token to be exchanged using the function `initiate()`.

```solidity
event Initiated(uint256 initiateTimestamp, bytes32 secretHash, address initiator, address participant, address initiatorAssetToken, uint256 initiatorAssetAmount, uint256 initiatorAssetRefundTimestamp);
```

#### Participated

This event indicates that `participant` has filled and locked the token to be exchanged using the function `participate()`.

```solidity
event Participated(uint256 participateTimestamp, bytes32 secretHash, address initiator, address participant, address participantAssetToken, uint256 participantAssetAmount, uint256 participantAssetRefundTimestamp);
```

#### PremiumFilled

This event indicates that `initiator` has filled and locked `premium` using the function `fillPremium()`.

```solidity
event PremiumFilled(uint256 fillPremiumTimestamp, bytes32 secretHash, address initiator, address participant, address premiumToken, uint256 premiumAmount, uint256 premiumRefundTimestamp);
```

#### InitiatorAssetRedeemed/ParticipantAssetRedeemed

These two events indicate that `asset` has been redeemed by the other party before the timelock by providing `secret`.

```solidity
event InitiatorAssetRedeemed(uint256 redeemTimestamp, bytes32 secretHash, bytes32 secret, address redeemer, address assetToken, uint256 amount);
```

```solidity
event ParticipantAssetRedeemed(uint256 redeemTimestamp, bytes32 secretHash, bytes32 secret, address redeemer, address assetToken, uint256 amount);
```

#### InitiatorAssetRefunded/ParticipantAssetRefunded

These two events indicate that `asset` has been refunded by the original owner after the timelock expires.

```solidity
event InitiatorAssetRefunded(uint256 refundTimestamp, bytes32 secretHash, address refunder, address assetToken, uint256 amount);
```

```solidity
event ParticipantAssetRefunded(uint256 refundTimestamp, bytes32 secretHash, address refunder, address assetToken, uint256 amount);
```

#### PremiumRedeemed

This event indicates that `premium` has been redeemed by `participant`. This implies that `asset` is either redeemed by `initiator` if it can provide the preimage of `secrectHash` before  `asset` timelock expires; or refunded by `participant` if `asset` timelock expires.

```solidity
event PremiumRedeemed(uint256 redeemTimestamp,bytes32 secretHash,address redeemer,address token,uint256 amount);
```

#### PremiumRefunded

This event indicates that `premium` has been refunded back to `initiator`, because of `participant` doesn&apos;t participate at all, by the time of `premium` timelock expires.

```solidity
event PremiumRefunded(uint256 refundTimestamp, bytes32 secretHash, address refunder, address token, uint256 amount);
```

## Rationale

+ To achieve the atomicity, HTLC is used.
+ The participant should decide whether to participate after the initiator locks the token and sets up the timelock.
+ The initiator should decide whether to proceed the swap (redeem the tokens from the participant and reveal the preimage of the hash lock), after the participant locks the tokens and sets up the time locks.
+ Premium is redeemable for the participant only if the participant participates in the swap and redeems the initiator&apos;s token before premium&apos;s timelock expires.
+ Premium is refundable for the initiator only if the initiator initiates but the participant does not participate in the swap at all.


## Security Considerations

+ The `initiateTimestamp` should cover the whole swap process.
+ The participant should never participate before the premium has been deposited.


## Backwards Compatibility

This proposal is fully backward compatible. Functionalities of existing standards will not be affected by this proposal, as it only provides additional features to them.


## Implementation

Please visit [here](/assets/sip-2266/Example.sol) to find our example implementation.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

## References

[^1]: [Hash Time Locked Contracts](https://en.bitcoin.it/wiki/Hash_Time_Locked_Contracts)

[^2]: [An Argument For Single-Asset Lightning Network](https://lists.linuxfoundation.org/pipermail/lightning-dev/2019-January/001798.html)

[^3]: [On the optionality and fairness of Atomic Swaps](https://eprint.iacr.org/2019/896)

      </description>
        <pubDate>Sat, 17 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-2266</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-2266</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Slashing Protection Interchange Format</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #3076 - Slashing Protection Interchange Format&lt;/strong&gt; is in Last Call status. It is authored by Michael Sproul (@michaelsproul), Sacha Saint-Leger (@sachayves), Danny Ryan (@djrtwo) and was originally created 2020-10-27. It is in the Interface category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-3076-validator-client-interchange-format-slashing-protection/4883&quot;&gt;https://sila-magicians.org/t/sip-3076-validator-client-interchange-format-slashing-protection/4883&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

A standard format for transferring a key&apos;s signing history allows validators to easily switch between clients without the risk of signing conflicting messages. While a common keystore format provides part of the solution, it does not contain any information about a key&apos;s signing history. For a validator moving their keys from client A to client B, this could lead to scenarios in which client B inadvertently signs a message that conflicts with an earlier message signed with client A. The interchange format described here provides a solution to this problem.

## Motivation

The proof of stake (PoS) protocol penalises validators for voting in ways that could result in two different versions of the chain being finalised. These types of penalties are called slashings.

For a validator following the protocol correctly, there is, in principle, no risk of being slashed. However, changing clients (from client A to client B, say) can result in a slashing risk if client B is unaware of the blocks and attestations that were signed with client A.

This can occur if client A and client B do not agree on what the present time is. For example, say client A&apos;s time is accidentally set to a day in the future (225 epochs), and a validator switches from client A to client B without giving B a record of the blocks and attestations signed with A. The validator in question now runs the risk of attesting to two different blocks in the same epoch (a slashable offence) for the next 225 epochs (since they&apos;ve already voted on these epochs with client A, and now stand to vote on them again with client B). Such time-skew bugs have been observed in the wild.

Another situation in which slashing protection is critical is in the case of re-orgs. During a re-org it is possible for a validator to be assigned new attestation duties for an epoch in which it has already signed an attestation. In this case it is essential that the record of the previous attestation is available, even if the validator just moved from one client to another in the space of a single epoch.

## Specification

### JSON Schema

A valid interchange file is one that adheres to the following JSON schema, and is interpreted according to the [Conditions](#conditions).

```json
{
  &quot;title&quot;: &quot;Signing history&quot;,
  &quot;description&quot;: &quot;This schema provides a record of the blocks and attestations signed by a set of validators&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;properties&quot;: {
    &quot;metadata&quot;: {
      &quot;type&quot;: &quot;object&quot;,
      &quot;properties&quot;: {
        &quot;interchange_format_version&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;description&quot;: &quot;The version of the interchange format that this document adheres to&quot;
        },
        &quot;genesis_validators_root&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;description&quot;: &quot;Calculated at Genesis time; serves to uniquely identify the chain&quot;
        }
      },
      &quot;required&quot;: [
        &quot;interchange_format_version&quot;,
        &quot;genesis_validators_root&quot;
      ]
    },
    &quot;data&quot;: {
      &quot;type&quot;: &quot;array&quot;,
      &quot;items&quot;: [
        {
          &quot;type&quot;: &quot;object&quot;,
          &quot;properties&quot;: {
            &quot;pubkey&quot;: {
              &quot;type&quot;: &quot;string&quot;,
              &quot;description&quot;: &quot;The BLS public key of the validator (encoded as a 0x-prefixed hex string)&quot;
            },
            &quot;signed_blocks&quot;: {
              &quot;type&quot;: &quot;array&quot;,
              &quot;items&quot;: [
                {
                  &quot;type&quot;: &quot;object&quot;,
                  &quot;properties&quot;: {
                    &quot;slot&quot;: {
                      &quot;type&quot;: &quot;string&quot;,
                      &quot;description&quot;: &quot;The slot number of the block that was signed&quot;
                    },
                    &quot;signing_root&quot;: {
                      &quot;type&quot;: &quot;string&quot;,
                      &quot;description&quot;: &quot;The output of compute_signing_root(block, domain)&quot;
                    }
                  },
                  &quot;required&quot;: [
                    &quot;slot&quot;
                  ]
                }
              ]
            },
            &quot;signed_attestations&quot;: {
              &quot;type&quot;: &quot;array&quot;,
              &quot;items&quot;: [
                {
                  &quot;type&quot;: &quot;object&quot;,
                  &quot;properties&quot;: {
                    &quot;source_epoch&quot;: {
                      &quot;type&quot;: &quot;string&quot;,
                      &quot;description&quot;: &quot;The attestation.data.source.epoch of the signed attestation&quot;
                    },
                    &quot;target_epoch&quot;: {
                      &quot;type&quot;: &quot;string&quot;,
                      &quot;description&quot;: &quot;The attestation.data.target.epoch of the signed attestation&quot;
                    },
                    &quot;signing_root&quot;: {
                      &quot;type&quot;: &quot;string&quot;,
                      &quot;description&quot;: &quot;The output of compute_signing_root(attestation, domain)&quot;
                    }
                  },
                  &quot;required&quot;: [
                    &quot;source_epoch&quot;,
                    &quot;target_epoch&quot;
                  ]
                }
              ]
            }
          },
          &quot;required&quot;: [
            &quot;pubkey&quot;,
            &quot;signed_blocks&quot;,
            &quot;signed_attestations&quot;
          ]
        }
      ]
    }
  },
  &quot;required&quot;: [
    &quot;metadata&quot;,
    &quot;data&quot;
  ]
}
```

### Example JSON Instance

```json
{
  &quot;metadata&quot;: {
    &quot;interchange_format_version&quot;: &quot;5&quot;,
    &quot;genesis_validators_root&quot;: &quot;0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673&quot;
  },
  &quot;data&quot;: [
    {
      &quot;pubkey&quot;: &quot;0xb845089a1457f811bfc000588fbb4e713669be8ce060ea6be3c6ece09afc3794106c91ca73acda5e5457122d58723bed&quot;,
      &quot;signed_blocks&quot;: [
        {
          &quot;slot&quot;: &quot;81952&quot;,
          &quot;signing_root&quot;: &quot;0x4ff6f743a43f3b4f95350831aeaf0a122a1a392922c45d804280284a69eb850b&quot;
        },
        {
          &quot;slot&quot;: &quot;81951&quot;
        }
      ],
      &quot;signed_attestations&quot;: [
        {
          &quot;source_epoch&quot;: &quot;2290&quot;,
          &quot;target_epoch&quot;: &quot;3007&quot;,
          &quot;signing_root&quot;: &quot;0x587d6a4f59a58fe24f406e0502413e77fe1babddee641fda30034ed37ecc884d&quot;
        },
        {
          &quot;source_epoch&quot;: &quot;2290&quot;,
          &quot;target_epoch&quot;: &quot;3008&quot;
        }
      ]
    }
  ]
}
```

### Conditions

After importing an interchange file with data field `data`, a signer must respect the following conditions:

1. Refuse to sign any block that is slashable with respect to the blocks contained in `data.signed_blocks`. For details of what constitutes a slashable block, see `process_proposer_slashing` (from `consensus-specs`). If the `signing_root` is absent from a block, a signer must assume that any new block with the same `slot` is slashable with respect to the imported block.

2. Refuse to sign any block with `slot &lt;= min(b.slot for b in data.signed_blocks if b.pubkey == proposer_pubkey)`, except if it is a repeat signing as determined by the `signing_root`.

3. Refuse to sign any attestation that is slashable with respect to the attestations contained in `data.signed_attestations`. For details of what constitutes a slashable attestation, see `is_slashable_attestation_data`.

4. Refuse to sign any attestation with source epoch less than the minimum source epoch present in that signer&apos;s attestations (as seen in `data.signed_attestations`). In pseudocode:

```python3
source.epoch &lt;
    min(att.source_epoch
        for att in data.signed_attestations
        if att.pubkey == attester_pubkey)
```

{:start=&quot;5&quot;}
5. Refuse to sign any attestation with target epoch less than or equal to the minimum target epoch present in that signer&apos;s attestations (as seen in `data.signed_attestations`), except if it is a repeat signing as determined by the `signing_root`. In pseudocode:

```python3
target_epoch &lt;=
    min(att.target_epoch
        for att in data.signed_attestations
        if att.pubkey == attester_pubkey)
```

### Additional Information

- The `interchange_format_version` version is set to 5.

- A signed block or attestation&apos;s `signing_root` refers to the message data (hash tree root) that gets signed with a BLS signature. It allows validators to re-sign and re-broadcast blocks or attestations if asked.

- The `signed_blocks` `signing_root`s are calculated using `compute_signing_root(block, domain)`: where `block` is the block (of type `BeaconBlock` or `BeaconBlockHeader`) that was signed, and `domain` is equal to `compute_domain(DOMAIN_BEACON_PROPOSER, fork, metadata.genesis_validators_root)`.

- The `signed_attestations` `signing_root`s are calculated using `compute_signing_root(attestation, domain)`: where `attestation` is the attestation (of type `AttestationData`) that was signed, and `domain` is equal to `compute_domain(DOMAIN_BEACON_ATTESTER, fork, metadata.genesis_validators_root)`.


## Rationale

### Supporting Different Strategies

The interchange format is designed to be flexible enough to support the full variety of slashing protection strategies that clients may implement, which may be categorised into two main types:

1. **Complete**: a database containing every message signed by each validator.
2. **Minimal**: a database containing only the latest messages signed by each validator.

The advantage of the minimal strategy is its simplicity and succinctness. Using only the latest messages for each validator, safe slashing protection can be achieved by refusing to sign messages for slots or epochs prior.

On the other hand, the complete strategy can provide safe slashing protection while also avoiding false positives (meaning that it only prevents a validator from signing if doing so would guarantee a slashing).

The two strategies are unified in the interchange format through the inclusion of [conditions](#conditions) (2), (4) and (5). This allows the interchange to transfer detailed or succinct information, as desired.

### Integer Representation

Most fields in the JSON schema are strings. For fields in which it is possible to encode the value as either a string or an integer, strings were chosen. This choice was made in order to avoid issues with different languages supporting different ranges of integers (specifically JavaScript, where the `number` type is a 64-bit float). If a validator is yet to sign a block or attestation, the relevant list is simply left empty.

### Versioning

The `interchange_format_version` is set to 5 because the specification went through several breaking changes during its design, incorporating feedback from implementers.


## Backwards Compatibility

This specification is not backwards-compatible with previous draft versions that used version numbers less than 5.


## Security Considerations

In order to minimise risk and complexity, the format has been designed to map cleanly onto the internal database formats used by implementers. Nevertheless, there are a few pitfalls worth illuminating.

### Advice for Complete Databases

For implementers who use a complete record of signed messages to implement their slashing protection database, we make the following recommendations:

- You MUST ensure that, in addition to importing all of the messages from an interchange, all the [conditions](#conditions) are enforced. In particular, conditions (2), (4) and (5) may not have been enforced by your implementation before adopting the interchange format. Our recommendation is to enforce these rules at all times, to keep the implementation clean and minimise the attack surface. For example: your slashing protection mechanism should not sign a block with a slot number less than, or equal to, the minimum slot number of a previously signed block, _irrespective_ of whether that minimum-slot block was imported from an interchange file, or inserted as part of your database&apos;s regular operation.
- If your database records the signing roots of messages in addition to their slot/epochs, you should ensure that imported messages without signing roots are assigned a suitable dummy signing root internally. We suggest using a special &quot;null&quot; value which is distinct from all other signing roots, although a value like `0x0` may be used instead (as it is extremely unlikely to collide with any real signing root).
- Care must be taken to avoid signing messages within a gap in the database (an area of unknown signing activity). This could occur if two interchanges were imported with a large gap between the last entry of the first and the first entry of the second. Signing in this gap is not safe, and would violate conditions (2), (4) and (5). It can be avoided by storing an explicit low watermark in addition to the actual messages of the slashing protection database, or by pruning on import so that the oldest messages from the interchange become the oldest messages in the database.

### Advice for Minimal Databases

For implementers who wish to implement their slashing protection database by storing only the latest block and attestation for each validator, we make the following recommendations:

- During import, make sure you take the _maximum_ slot block and _maximum_ source and target attestations for each validator. Although the [conditions](#conditions) require the minimums to be enforced, taking the maximums from an interchange file and merging them with any existing values in the database is the recommended approach. For example, if the interchange file includes blocks for validator `V` at slots 4, 98 and 243, then the latest signed block for validator `V` should be updated to the one from slot 243.  However, if the database has already included a block for this validator at a slot greater than 243, for example, slot 351, then the database&apos;s existing value should remain unchanged.

### General Recommendations

- To avoid exporting an outdated interchange file -- an action which creates a slashing risk -- your implementation should only allow the slashing protection database to be exported when the validator client or signer is _stopped_ -- in other words, when the client or signer is no longer adding new messages to the database.
- Similarly, your implementation should only allow an interchange file to be imported when the validator client is stopped.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Tue, 27 Oct 2020 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-3076</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-3076</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>SVM trace specification</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #3155 - SVM trace specification&lt;/strong&gt; is in Last Call status. It is authored by Martin Holst Swende (@holiman), Marius van der Wijden (@MariusVanDerWijden) and was originally created 2020-12-07. It is in the Interface category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-3155-create-svm-trace-specification/5007&quot;&gt;https://sila-magicians.org/t/sip-3155-create-svm-trace-specification/5007&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

Introduce a new JSON standard for SVM traces during execution of state tests.

## Motivation

The Sila Virtual Machine executes all smart contract code on sila.
In order to debug smart contracts and state tests better, a common format was introduced to log every execution step of the SVM.
This format was implemented by Go-Sila, Parity-Sila, Nethermind and Besu.
Since the common format was not well-defined, the implementations differed slightly, making it hard to develop adequate tooling which reduces the usefulness of tracing significantly.

This SIP has multiple goals:

- Move the specification to a more visible place to encourage new clients to implement it
- Strictly define corner cases that were not addressed in the previous version
- Allow for updates to the specification in case new fields are introduced during execution
- Provide sample output

Implementing this SIP in all major clients allows us to create meaningful differential fuzzers that fuzz SVM implementations for the sila-mainnet and all upcoming hardforks.
It also helps to find differences in execution quickly in the case of a chain split.

This SIP will enable users to create better differential fuzzing infrastructure to compare the SVM implementations of all major Sila clients against each other.
This could help to find bugs that are currently present in the client implementations.

## Specification

Clients should be able to execute simple transactions as well as code and return traces. In the following, we will call this client CUT (client under test) and use go-sila&apos;s
`svm` binary for code examples.

### Datatypes

| Type       | Explanation                                                    | Example             |
|------------|----------------------------------------------------------------|---------------------|
| Number     | Plain json number                                              | &quot;pc&quot;:0              |
| Hex-Number | Hex-encoded number                                             | &quot;gas&quot;:&quot;0x2540be400&quot; |
| String     | Plain string                                                   | &quot;opName&quot;:&quot;PUSH1&quot;    |
| Hex-String | Hex-encoded string                                             |                     |
| Array of x | Array of x encoded values                                      |                     |
| Key-Value  | Key-Value structure with key and values encoded as hex strings |                     |
| Boolean    | Json bool can either be true or false                          | &quot;pass&quot;: true        |

### Output

The CUT MUST output a `json` object for EACH operation.

#### Required Fields

| Name         | Type                 | Explanation                              |
|--------------|----------------------|------------------------------------------|
| `pc`         | Number               | Program Counter                          |
| `op`         | Number               | OpCode                                   |
| `gas`        | Hex-Number           | Gas left before executing this operation |
| `gasCost`    | Hex-Number           | Gas cost of this operation               |
| `memSize`    | Number               | Size of memory array                     |
| `stack`      | Array of Hex-Numbers | Array of all values on the stack         |
| `depth`      | Number               | Depth of the call stack                  |
| `returnData` | Hex-String           | Data returned by function call           |
| `refund`     | Number               | Amount of **global** gas refunded        |

#### Optional Fields

| Name          | Type                 | Explanation                                                         |
|---------------|----------------------|---------------------------------------------------------------------|
| `opName`      | String               | Name of the operation                                               |
| `error`       | Hex-String           | Description of an error (should contain revert reason if supported) |
| `memory`      | Array of Hex-Strings | Array of all allocated values                                       |
| `storage`     | Key-Value            | Array of all stored values                                          |

*Example:*

```
{&quot;pc&quot;:0,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540be400&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x&quot;,&quot;memSize&quot;:0,&quot;stack&quot;:[],&quot;depth&quot;:1,&quot;error&quot;:null,&quot;opName&quot;:&quot;PUSH1&quot;}
```

- The `stack`, `memory` and `memSize` are the values *before* execution of the op.
- All array attributes (`stack`, `memory`) MUST be initialized to empty arrays (`&quot;stack&quot;:[]`) NOT to null.
- If the CUT will not be outputting values for `memory` or `storage` then the `memory` and `storage` fields are omitted.
  This can happen either because the CUT does not support tracing these fields or it has been configured not to trace it.
- The `memSize` field MUST be present regardless of `memory` support.
- Clients SHOULD implement a way to disable recording the storage as the stateroot includes all storage updates.
- Clients SHOULD output the fields in the same order as listed in this SIP.

The CUT MUST NOT output a line for the `STOP` operation if an error occurred:
  
*Example:*

```
{&quot;pc&quot;:2,&quot;op&quot;:0,&quot;gas&quot;:&quot;0x2540be3fd&quot;,&quot;gasCost&quot;:&quot;0x0&quot;,&quot;memory&quot;:&quot;0x&quot;,&quot;memSize&quot;:0,&quot;stack&quot;:[&quot;0x40&quot;],&quot;depth&quot;:1,&quot;error&quot;:null,&quot;opName&quot;:&quot;STOP&quot;}
```

### Summary and Error Handling

At the end of execution, the CUT MUST print summary info; this info SHOULD have the following fields.
The summary should be a single `jsonl` object.

#### Required Fields

| Name        | Type       | Explanation                                            |
|-------------|------------|--------------------------------------------------------|
| `stateRoot` | Hex-String | Root of the state trie after executing the transaction |
| `output`    |            | Return values of the function                          |
| `gasUsed`   | Hex-Number | All gas used by the transaction                        |
| `pass`      | Boolean    | Bool whether transaction was executed successfully     |

#### Optional Fields

| Name   | Type   | Explanation                                           |
|--------|--------|-------------------------------------------------------|
| `time` | Number | Time in nanoseconds needed to execute the transaction |
| `fork` | String | Name of the fork rules used for execution             |

*Example*:

```
{&quot;stateRoot&quot;:&quot;0xd4c577737f5d20207d338c360c42d3af78de54812720e3339f7b27293ef195b7&quot;,&quot;output&quot;:&quot;&quot;,&quot;gasUsed&quot;:&quot;0x3&quot;,&quot;pass&quot;:&quot;true&quot;,&quot;time&quot;:141485}
```

## Rationale

This SIP is largely based on the previous non-official documentation for SVM tracing.
It tries to cover as many corner cases as possible to enable true client compatibility.
The datatypes and if a field is optional is chosen to be as compatible with current implementations as possible.

## Backwards Compatibility

This SIP is fully backward compatible with sila as it only introduces a better tracing infrastructure that is optional for clients to implement.

### Clients

This SIP is fully backward compatible with go-sila. Sila, Besu and Nethermind clients would have to change their JSON output of
`sila-svm` `SVMtool` and
`nethtest` slightly do adhere to the new and stricter specs. New clients would need to implement this change if they want to be part of the differential fuzzing group.

## Test Cases

```bash
${BESU_HOME}/bin/SVMtool --code 0x604080536040604055604060006040600060025afa6040f3 {&quot;pc&quot;:0,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540be400&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memSize&quot;:0,&quot;stack&quot;:[],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:2,&quot;op&quot;:128,&quot;gas&quot;:&quot;0x2540be3fd&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memSize&quot;:0,&quot;stack&quot;:[&quot;0x40&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;DUP1&quot;}
{&quot;pc&quot;:3,&quot;op&quot;:83,&quot;gas&quot;:&quot;0x2540be3fa&quot;,&quot;gasCost&quot;:&quot;0xc&quot;,&quot;memSize&quot;:0,&quot;stack&quot;:[&quot;0x40&quot;,&quot;0x40&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;MSTORE8&quot;}
{&quot;pc&quot;:4,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540be3ee&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:6,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540be3eb&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:8,&quot;op&quot;:85,&quot;gas&quot;:&quot;0x2540be3e8&quot;,&quot;gasCost&quot;:&quot;0x4e20&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;,&quot;0x40&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;SSTORE&quot;}
{&quot;pc&quot;:9,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540b95c8&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:11,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540b95c5&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:13,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540b95c2&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;,&quot;0x0&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:15,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540b95bf&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;,&quot;0x0&quot;,&quot;0x40&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:17,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540b95bc&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;,&quot;0x0&quot;,&quot;0x40&quot;,&quot;0x0&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:19,&quot;op&quot;:90,&quot;gas&quot;:&quot;0x2540b95b9&quot;,&quot;gasCost&quot;:&quot;0x2&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;,&quot;0x0&quot;,&quot;0x40&quot;,&quot;0x0&quot;,&quot;0x2&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;GAS&quot;}
{&quot;pc&quot;:20,&quot;op&quot;:250,&quot;gas&quot;:&quot;0x2540b95b7&quot;,&quot;gasCost&quot;:&quot;0x24abb676c&quot;,&quot;memory&quot;:&quot;0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x40&quot;,&quot;0x0&quot;,&quot;0x40&quot;,&quot;0x0&quot;,&quot;0x2&quot;,&quot;0x2540b95b7&quot;],&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;STATICCALL&quot;}
{&quot;pc&quot;:21,&quot;op&quot;:96,&quot;gas&quot;:&quot;0x2540b92a7&quot;,&quot;gasCost&quot;:&quot;0x3&quot;,&quot;memory&quot;:&quot;0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x1&quot;],&quot;returnData&quot;:&quot;0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b&quot;,&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;PUSH1&quot;}
{&quot;pc&quot;:23,&quot;op&quot;:243,&quot;gas&quot;:&quot;0x2540b92a4&quot;,&quot;gasCost&quot;:&quot;0x0&quot;,&quot;memory&quot;:&quot;0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;memSize&quot;:96,&quot;stack&quot;:[&quot;0x1&quot;,&quot;0x40&quot;],&quot;returnData&quot;:&quot;0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b&quot;,&quot;depth&quot;:1,&quot;refund&quot;:0,&quot;opName&quot;:&quot;RETURN&quot;}
{&quot;stateRoot&quot;:&quot;0x8fa0dcc7f1d2383c89e5737c2843632db881c0946e80b71fe7175365e6538797&quot;,&quot;output&quot;:&quot;0x40&quot;,&quot;gasUsed&quot;:&quot;0x515c&quot;,&quot;pass&quot;:true,&quot;fork&quot;:&quot;Istanbul&quot;}
```

## Security Considerations

Tracing is expensive.

Exposing an endpoint for creating traces publicly could open up a denial of service vector.

Clients should consider putting trace endpoints behind a separate flag from other endpoints.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Mon, 07 Dec 2020 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-3155</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-3155</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>SRC-721 Nonce Extension</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #5008 - SRC-721 Nonce Extension&lt;/strong&gt; is in Last Call status. It is authored by Anders (@0xanders), Lance (@LanceSnow), Shrug &lt;shrug@emojidao.org&gt; and was originally created 2022-04-10. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip5008-sip-721-nonce-and-metadata-update-extension/8925&quot;&gt;https://sila-magicians.org/t/sip5008-sip-721-nonce-and-metadata-update-extension/8925&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This standard is an extension of [SRC-721](/SIPS/sip-721). It proposes adding a `nonce` function to SRC-721 tokens.

## Motivation

Some orders of NFT marketplaces have been attacked and the NFTs sold at a lower price than the current market floor price. This can happen when users transfer an NFT to another wallet and, later, back to the original wallet. This reactivates the order, which may list the token at a much lower price than the owner would have intended.

This SIP proposes adding a `nonce` property to SRC-721 tokens, and the `nonce` will be changed when a token is transferred. If a `nonce` is added to an order, the order can be checked to avoid attacks.

## Specification

The keywords &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot; and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119.

```solidity

/// @dev the SRC-165 identifier for this interface is 0xce03fdab.
interface ISRC5008 /* is ISRC165 */ {
    /// @notice Emitted when the `nonce` of an NFT is changed
    event NonceChanged(uint256 tokenId, uint256 nonce);

    /// @notice Get the nonce of an NFT
    /// Throws if `tokenId` is not a valid NFT
    /// @param tokenId The id of the NFT
    /// @return The nonce of the NFT
    function nonce(uint256 tokenId) external view returns(uint256);
}
```

The `nonce(uint256 tokenId)` function MUST be implemented as `view`.

The `supportsInterface` method MUST return `true` when called with `0xce03fdab`.

## Rationale

At first `transferCount` was considered as function name, but there may some case to change the `nonce` besides transfer, such as important properties changed, then we changed `transferCount` to `nonce`.

## Backwards Compatibility

This standard is compatible with SRC-721.

## Test Cases

Test cases are included in [test.js](/assets/sip-5008/test/test.ts).

Run:

```sh
cd ../assets/sip-5008
npm install
npm run test
```

## Reference Implementation

See [`SRC5008.sol`](/assets/sip-5008/contracts/SRC5008.sol).

## Security Considerations

No security issues found.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Sun, 10 Apr 2022 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-5008</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-5008</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Soulbound Badge</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #5114 - Soulbound Badge&lt;/strong&gt; is in Last Call status. It is authored by Micah Zoltu (@MicahZoltu) and was originally created 2022-05-30. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-5114-soulbound-token/9417&quot;&gt;https://sila-magicians.org/t/sip-5114-soulbound-token/9417&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

A soulbound badge is a token that, when minted, is bound to another Non-Fungible Token (NFT), and cannot be transferred/moved after that.


## Specification

```solidity
interface ISRC5114 {
	// fired anytime a new instance of this badge is minted
	// this event **MUST NOT** be fired twice for the same `badgeId`
	event Mint(uint256 indexed badgeId, address indexed nftAddress, uint256 indexed nftTokenId);

	// returns the NFT that this badge is bound to.
	// this function **MUST** throw if the badge hasn&apos;t been minted yet
	// this function **MUST** always return the same result every time it is called after it has been minted
	// this function **MUST** return the same value as found in the original `Mint` event for the badge
	function ownerOf(uint256 badgeId) external view returns (address nftAddress, uint256 nftTokenId);

	// returns a URI with details about this badge collection
	// the metadata returned by this is merged with the metadata return by `badgeUri(uint256)`
	// the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://)
	// the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://)
	// data from `badgeUri` takes precedence over data returned by this method
	// any external links referenced by the content at `collectionUri` also **MUST** follow all of the above rules
	function collectionUri() external pure returns (string collectionUri);

	// returns a censorship resistant URI with details about this badge instance
	// the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://)
	// the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://)
	// data from this takes precedence over data returned by `collectionUri`
	// any external links referenced by the content at `badgeUri` also **MUST** follow all of the above rules
	function badgeUri(uint256 badgeId) external view returns (string badgeUri);

	// returns a string that indicates the format of the `badgeUri` and `collectionUri` results (e.g., &apos;SIP-ABCD&apos; or &apos;soulbound-schema-version-4&apos;)
	function metadataFormat() external pure returns (string format);
}
```

Implementers of this standard **SHOULD** also depend on a standard for interface detection so callers can easily find out if a given contract implements this interface.


## Rationale

### Immutability

By requiring that badges can never move, we both guarantee non-separability and non-mergeability among collections of soulbound badges that are bound to a single NFT while simultaneously allowing users to aggressively cache results.

### Content Addressable URIs Required

Soulbound badges are meant to be permanent badges/indicators attached to a persona.
This means that not only can the user not transfer ownership, but the minter also cannot withdraw/transfer/change ownership as well.
This includes mutating or removing any remote content as a means of censoring or manipulating specific users.

### No Specification for `badgeUri` Data Format

The format of the data pointed to by `collectionUri()` and `badgeUri(uint256)`, and how to merge them, is intentionally left out of this standard in favor of separate standards that can be iterated on in the future.
The immutability constraints are the only thing defined by this to ensure that the spirit of this badge is maintained, regardless of the specifics of the data format.
The `metadataFormat` function can be used to inform a caller what type/format/version of data they should expect at the URIs, so the caller can parse the data directly without first having to deduce its format via inspection.


## Backwards Compatibility

This is a new token type and is not meant to be backward compatible with any existing tokens other than existing viable souls (any asset that can be identified by `[address,id]`).


## Security Considerations

Users of badges that claim to implement this SIP must be diligent in verifying they actually do.
A badge author can create a badge that, upon initial probing of the API surface, may appear to follow the rules when in reality it doesn&apos;t.
For example, the contract could allow transfers via some mechanism and simply not utilize them initially.

It should also be made clear that soulbound badges are not bound to a human, they are bound to a persona.
A persona is any actor (which could be a group of humans) that collects multiple soulbound badges over time to build up a collection of badges.
This persona may transfer to another human, or to another group of humans, and anyone interacting with a persona should not assume that there is a single permanent human behind that persona.

It is possible for a soulbound badge to be bound to another soulbound badge.
In theory, if all badges in the chain are created at the same time they could form a loop.
Software that tries to walk such a chain should take care to have an exit strategy if a loop is detected.


## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-5114</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-5114</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Cross-Chain Execution</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #5164 - Cross-Chain Execution&lt;/strong&gt; is in Last Call status. It is authored by Brendan Asselstine (@asselstine), Pierrick Turelier (@PierrickGT), Chris Whinfrey (@cwhinfrey) and was originally created 2022-06-14. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-5164-cross-chain-execution/9658&quot;&gt;https://sila-magicians.org/t/sip-5164-cross-chain-execution/9658&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This specification defines a cross-chain execution interface for SVM-based blockchains. Implementations of this specification will allow contracts on one chain to call contracts on another by sending a cross-chain message.

The specification defines two components: the &quot;Message Dispatcher&quot; and the &quot;Message Executor&quot;. The Message Dispatcher lives on the calling side, and the executor lives on the receiving side. When a message is sent, a Message Dispatcher will move the message through a transport layer to a Message Executor, where they are executed. Implementations of this specification must implement both components.

## Motivation

Many Sila protocols need to coordinate state changes across multiple SVM-based blockchains. These chains often have native or third-party bridges that allow Sila contracts to execute code. However, bridges have different APIs so bridge integrations are custom. Each one affords different properties; with varying degrees of security, speed, and control. Defining a simple, common specification will increase code re-use and allow us to use common bridge implementations.

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

This specification allows contracts on one chain to send messages to contracts on another chain. There are two key interfaces that needs to be implemented:

- `MessageDispatcher`
- `MessageExecutor`

The `MessageDispatcher` lives on the origin chain and dispatches messages to the `MessageExecutor` for execution. The `MessageExecutor` lives on the destination chain and executes dispatched messages.

### MessageDispatcher

The `MessageDispatcher` lives on the chain from which messages are sent. The Dispatcher&apos;s job is to broadcast messages through a transport layer to one or more `MessageExecutor` contracts.

A unique `messageId` MUST be generated for each message or message batch. The message identifier MUST be unique across chains and dispatchers.  This can be achieved by hashing a tuple of `chainId, dispatcherAddress, messageNonce` where messageNonce is a monotonically increasing integer per message.

#### MessageDispatcher Methods

**dispatchMessage**

Will dispatch a message to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`.

`MessageDispatcher`s MUST emit the `MessageDispatched` event when a message is dispatched.

`MessageDispatcher`s MUST revert if `toChainId` is not supported.

`MessageDispatcher`s MUST forward the message to a `MessageExecutor` on the `toChainId`.

`MessageDispatcher`s MUST use a unique `messageId` for each message.

`MessageDispatcher`s MUST return the `messageId` to allow the message sender to track the message.

`MessageDispatcher`s MAY require payment.

```solidity
interface MessageDispatcher {
  function dispatchMessage(uint256 toChainId, address to, bytes calldata data) external payable returns (bytes32 messageId);
}
```

```yaml
- name: dispatchMessage
  type: function
  stateMutability: payable
  inputs:
    - name: toChainId
      type: uint256
    - name: to
      type: address
    - name: data
      type: bytes
  outputs:
    - name: messageId
      type: bytes32
```

#### MessageDispatcher Events

**MessageDispatched**

The `MessageDispatched` event MUST be emitted by the `MessageDispatcher` when an individual message is dispatched.

```solidity
interface MessageDispatcher {
  event MessageDispatched(
    bytes32 indexed messageId,
    address indexed from,
    uint256 indexed toChainId,
    address to,
    bytes data,
  );
}
```

```yaml
- name: MessageDispatched
  type: event
  inputs:
    - name: messageId
      indexed: true
      type: bytes32
    - name: from
      indexed: true
      type: address
    - name: toChainId
      indexed: true
      type: uint256
    - name: to
      type: address
    - name: data
      type: bytes
```

### MessageExecutor

The `MessageExecutor` executes dispatched messages and message batches. Developers must implement a `MessageExecutor` in order to execute messages on the receiving chain.

The `MessageExecutor` will execute a messageId only once, but may execute messageIds in any order. This specification makes no ordering guarantees, because messages and message batches may travel non-sequentially through the transport layer.

#### Execution

`MessageExecutor`s SHOULD verify all message data with the bridge transport layer.

`MessageExecutor`s MUST NOT successfully execute a message more than once.

`MessageExecutor`s MUST revert the transaction when a message fails to be executed allowing the message to be retried at a later time.

**Calldata**

`MessageExecutor`s MUST append the ABI-packed (`messageId`, `fromChainId`, `from`) to the calldata for each message being executed. This allows the receiver of the message to verify the cross-chain sender and the chain that the message is coming from.

```solidity
to.call(abi.encodePacked(data, messageId, fromChainId, from));
```

```yaml
- name: calldata
  type: bytes
  inputs:
    - name: data
      type: bytes
    - name: messageId
      type: bytes32
    - name: fromChainId
      type: uint256
    - name: from
      type: address
```

#### MessageExecutor Events

**MessageIdExecuted**

`MessageIdExecuted` MUST be emitted once a message or message batch has been executed.

```solidity
interface MessageExecutor {
  event MessageIdExecuted(
    uint256 indexed fromChainId,
    bytes32 indexed messageId
  );
}
```

```yaml
- name: MessageIdExecuted
  type: event
  inputs:
    - name: fromChainId
      indexed: true
      type: uint256
    - name: messageId
      indexed: true
      type: bytes32
```

#### MessageExecutor Errors

**MessageAlreadyExecuted**

`MessageExecutor`s MUST revert if a messageId has already been executed and SHOULD emit a `MessageIdAlreadyExecuted` custom error.

```solidity
interface MessageExecutor {
  error MessageIdAlreadyExecuted(
    bytes32 messageId
  );
}
```

**MessageFailure**

`MessageExecutor`s MUST revert if an individual message fails and SHOULD emit a `MessageFailure` custom error.

```solidity
interface MessageExecutor {
  error MessageFailure(
    bytes32 messageId,
    bytes errorData
  );
}
```

## Rationale

The `MessageDispatcher` can be coupled to one or more `MessageExecutor`. It is up to bridges to decide how to couple the two. Users can easily bridge a message by calling `dispatchMessage` without being aware of the `MessageExecutor` address. Messages can also be traced by a client using the data logged by the `MessageIdExecuted` event.

Some bridges may require payment in the native currency, so the `dispatchMessage` function is payable.

## Backwards Compatibility

This specification is compatible with existing governance systems as it offers simple cross-chain execution.

## Security Considerations

Bridge trust profiles are variable, so users must understand that bridge security depends on the implementation.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Tue, 14 Jun 2022 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-5164</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-5164</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>SRC-1155 Allowance Extension</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #5216 - SRC-1155 Allowance Extension&lt;/strong&gt; is in Last Call status. It is authored by Iván Mañús (@ivanmmurciaua), Juan Carlos Cantó (@EscuelaCryptoES) and was originally created 2022-07-11. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-src1155-approval-by-amount/9898&quot;&gt;https://sila-magicians.org/t/sip-src1155-approval-by-amount/9898&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SRC defines standard functions for granular approval of [SRC-1155](/SIPS/sip-1155) tokens by both `id` and `amount`. This SRC extends [SRC-1155](/SIPS/sip-1155).

## Motivation

[SRC-1155](/SIPS/sip-1155)&apos;s popularity means that multi-token management transactions occur on a daily basis. Although it can be used as a more comprehensive alternative to [SRC-721](/SIPS/sip-721), SRC-1155 is most commonly used as intended: creating multiple `id`s, each with multiple tokens. While many projects interface with these semi-fungible tokens, by far the most common interactions are with NFT marketplaces.

Due to the nature of the blockchain, programming errors or malicious operators can cause permanent loss of funds. It is therefore essential that transactions are as trustless as possible. SRC-1155 uses the `setApprovalForAll` function, which approves ALL tokens with a specific `id`. This system has obvious minimum required trust flaws. This SRC combines ideas from [SRC-20](/SIPS/sip-20) and [SRC-721](/SIPS/sip-721) in order to create a trust mechanism where an owner can allow a third party, such as a marketplace, to approve a limited (instead of unlimited) number of tokens of one `id`.

## Specification

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Contracts using this SRC MUST implement the `ISRC5216` interface.

### Interface implementation

```solidity
/**
 * @title SRC-1155 Allowance Extension
 * Note: the SRC-165 identifier for this interface is 0x1be07d74
 */
interface ISRC5216 is ISRC1155 {

    /**
     * @notice Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `id` and with an amount: `amount`.
     */
    event Approval(address indexed account, address indexed operator, uint256 id, uint256 amount);

    /**
     * @notice Grants permission to `operator` to transfer the caller&apos;s tokens, according to `id`, and an amount: `amount`.
     * Emits an {Approval} event.
     *
     * Requirements:
     * - `operator` cannot be the caller.
     */
    function approve(address operator, uint256 id, uint256 amount) external;

    /**
     * @notice Returns the amount allocated to `operator` approved to transfer `account`&apos;s tokens, according to `id`.
     */
    function allowance(address account, address operator, uint256 id) external view returns (uint256);
}
```

The `approve(address operator, uint256 id, uint256 amount)` function MUST be either `public` or `external`.

The `allowance(address account, address operator, uint256 id)` function MUST be either `public` or `external` and MUST be `view`.

The `safeTrasferFrom` function (as defined by SRC-1155) MUST:

- Not revert if the user has approved `msg.sender` with a sufficient `amount`
- Subtract the transferred amount of tokens from the approved amount if `msg.sender` is not approved with `setApprovalForAll`

In addition, the `safeBatchTransferFrom` MUST:

- Add an extra condition that checks if the `allowance` of all `ids` have the approved `amounts` (See `_checkApprovalForBatch` function reference implementation)

The `Approval` event MUST be emitted when a certain number of tokens are approved.

The `supportsInterface` method MUST return `true` when called with `0x1be07d74`.

## Rationale

The name &quot;SRC-1155 Allowance Extension&quot; was chosen because it is a succinct description of this SRC. Users can approve their tokens by `id` and `amount` to `operator`s.

By having a way to approve and revoke in a manner similar to [SRC-20](/SIPS/sip-20), the trust level can be more directly managed by users:

- Using the `approve` function, users can approve an operator to spend an `amount` of tokens for each `id`.
- Using the `allowance` function, users can see the approval that an operator has for each `id`.

The [SRC-20](/SIPS/sip-20) name patterns were used due to similarities with [SRC-20](/SIPS/sip-20) approvals.

## Backwards Compatibility

This standard is compatible with [SRC-1155](/SIPS/sip-1155).

## Reference Implementation

The reference implementation can be found [here](/assets/sip-5216/SRC5216.sol).

## Security Considerations

Users of this SRC must thoroughly consider the amount of tokens they give permission to `operators`, and should revoke unused authorizations.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Mon, 11 Jul 2022 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-5216</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-5216</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Endorsement - Permit for Any Functions</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #5453 - Endorsement - Permit for Any Functions&lt;/strong&gt; is in Last Call status. It is authored by Zainan Victor Zhou (@xinbenlv) and was originally created 2022-08-12. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/src-5453-endorsement-standard/10355&quot;&gt;https://sila-magicians.org/t/src-5453-endorsement-standard/10355&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP establishes a general protocol for permitting and approving function calls in the same transaction relying on [SRC-5750](/SIPS/sip-5750).
Unlike a few prior art ([SRC-2612](/SIPS/sip-2612) for [SRC-20](/SIPS/sip-20), [SRC-4494](/SIPS/sip-4494) for [SRC-721](/SIPS/sip-721) that
usually only permit for a single behavior (`transfer` for SRC-20 and `safeTransferFrom` for SRC-721) and a single approver in two transactions (first a `permit(...)` TX, then a `transfer`-like TX), this SIP provides a way to permit arbitrary behaviors and aggregating multiple approvals from arbitrary number of approvers in the same transaction, allowing for Multi-Sig or Threshold Signing behavior.

## Motivation

1. Support permit(approval) alongside a function call.
2. Support a second approval from another user.
3. Support pay-for-by another user
4. Support multi-sig
5. Support persons acting in concert by endorsements
6. Support accumulated voting
7. Support off-line signatures

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

### Interfaces

The interfaces and structures referenced here are as follows

```solidity
pragma solidity ^0.8.9;

struct ValidityBound {
    bytes32 functionParamStructHash;
    uint256 validSince;
    uint256 validBy;
    uint256 nonce;
}

struct SingleEndorsementData {
    address endorserAddress; // 32
    bytes sig; // dynamic = 65
}

struct GeneralExtensionDataStruct {
    bytes32 src5453MagicWord;
    uint256 src5453Type;
    uint256 nonce;
    uint256 validSince;
    uint256 validBy;
    bytes endorsementPayload;
}

interface ISRC5453EndorsementCore {
    function sip5453Nonce(address endorser) external view returns (uint256);
    function isEligibleEndorser(address endorser) external view returns (bool);
}

interface ISRC5453EndorsementDigest {
    function computeValidityDigest(
        bytes32 _functionParamStructHash,
        uint256 _validSince,
        uint256 _validBy,
        uint256 _nonce
    ) external view returns (bytes32);

    function computeFunctionParamHash(
        string memory _functionName,
        bytes memory _functionParamPacked
    ) external view returns (bytes32);
}

interface ISRC5453EndorsementDataTypeA {
    function computeExtensionDataTypeA(
        uint256 nonce,
        uint256 validSince,
        uint256 validBy,
        address endorserAddress,
        bytes calldata sig
    ) external view returns (bytes memory);
}


interface ISRC5453EndorsementDataTypeB {
    function computeExtensionDataTypeB(
        uint256 nonce,
        uint256 validSince,
        uint256 validBy,
        address[] calldata endorserAddress,
        bytes[] calldata sigs
    ) external view returns (bytes memory);
}
```

See [`ISRC5453.sol`](/assets/sip-5453/ISRC5453.sol).

### Behavior specification

As specified in [SRC-5750 General Extensibility for Method Behaviors](/SIPS/sip-5750), any compliant method that has a `bytes extraData` parameter as its
last designated parameter for extending behaviors can conform to [SRC-5453](/SIPS/sip-5453) as the way to indicate a permit from a certain user.

1. Any compliant method of this SIP MUST be a [SRC-5750](/SIPS/sip-5750) compliant method.
2. Caller MUST pass in the last parameter `bytes extraData` conforming to Solidity memory-encoded bytes of `GeneralExtensionDataStruct` specified in _Section Interfaces_. The following descriptions are based on when decoding `bytes extraData` into a `GeneralExtensionDataStruct`
3. In the `GeneralExtensionDataStruct`-decoded `extraData`, caller MUST set the value of `GeneralExtensionDataStruct.src5453MagicWord` to be the `keccak256(&quot;SRC5453-ENDORSEMENT&quot;)`.
4. Caller MUST set the value of `GeneralExtensionDataStruct.src5453Type` to be one of the supported values.

```solidity
uint256 constant SRC5453_TYPE_A = 1;
uint256 constant SRC5453_TYPE_B = 2;
```

5. When the value of `GeneralExtensionDataStruct.src5453Type` is set to be `SRC5453_TYPE_A`, `GeneralExtensionDataStruct.endorsementPayload` MUST be abi encoded bytes of a `SingleEndorsementData`.
6. When the value of `GeneralExtensionDataStruct.src5453Type` is set to be `SRC5453_TYPE_B`, `GeneralExtensionDataStruct.endorsementPayload` MUST be abi encoded bytes of `SingleEndorsementData[]` (a dynamic array).

7. Each `SingleEndorsementData` MUST have a `address endorserAddress;` and a 65-bytes `bytes sig` signature.

8. Each `bytes sig` MUST be an ECDSA (secp256k1) signature using private key of signer whose corresponding address is `endorserAddress` signing `validityDigest` which is the hashTypeDataV4 of [SIP-712](/SIPS/sip-712) of hashStruct of `ValidityBound` data structure as follows:

```solidity
bytes32 validityDigest =
    sip712HashTypedDataV4(
        keccak256(
            abi.encode(
                keccak256(
                    &quot;ValidityBound(bytes32 functionParamStructHash,uint256 validSince,uint256 validBy,uint256 nonce)&quot;
                ),
                functionParamStructHash,
                _validSince,
                _validBy,
                _nonce
            )
        )
    );
```

9. The `functionParamStructHash` MUST be computed as follows

```solidity
        bytes32 functionParamStructHash = keccak256(
            abi.encodePacked(
                keccak256(bytes(_functionStructure)),
                _functionParamPacked
            )
        );
        return functionParamStructHash;
```

whereas

- `_functionStructure` MUST be computed as `function methodName(type1 param1, type2 param2, ...)`.
- `_functionParamPacked` MUST be computed as `enc(param1) || enco(param2) ...`

10. Upon validating that `endorserAddress == ecrecover(validityDigest, signature)` or `SIP1271(endorserAddress).isValidSignature(validityDigest, signature) == SRC1271.MAGICVALUE`, the single endorsement MUST be deemed valid.
11. Compliant method MAY choose to impose a threshold for a number of endorsements needs to be valid in the same `SRC5453_TYPE_B` kind of `endorsementPayload`.

12. The `validSince` and `validBy` are both inclusive. Implementer MAY choose to use blocknumber or timestamp. Implementors SHOULD find a way to indicate whether `validSince` and `validBy` is blocknumber or timestamp.

## Rationale

1. We chose to have both `SRC5453_TYPE_A`(single-endorsement) and `SRC5453_TYPE_B`(multiple-endorsements, same nonce for entire contract) so we
could balance a wider range of use cases. E.g. the same use cases of SRC-2612 and [SRC-4494](/SIPS/sip-4494) can be supported by `SRC5453_TYPE_A`. And threshold approvals can be done via `SRC5453_TYPE_B`. More complicated approval types can also be extended by defining new `SRC5453_TYPE_?`

2. We chose to include both `validSince` and `validBy` to allow maximum flexibility in expiration. This can also be supported natively by the SVM if [SRC-5081](/SIPS/sip-5081) is adopted, but [SRC-5081](/SIPS/sip-5081) will not be adopted anytime soon, so we choose to add these two numbers in our protocol to allow
smart contract level support.

## Backwards Compatibility

The design assumes a `bytes calldata extraData` to maximize the flexibility of future extensions. This assumption is compatible with [SRC-721](/SIPS/sip-721), [SRC-1155](/SIPS/sip-1155) and many other SRC-track SIPs. Those that aren&apos;t, such as [SRC-20](/SIPS/sip-20), can also be updated to support it, such as using a wrapper contract or proxy upgrade.

## Reference Implementation

In addition to the specified algorithm for validating endorser signatures, we also present the following reference implementations.

```solidity
pragma solidity ^0.8.9;

import &quot;@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol&quot;;
import &quot;@openzeppelin/contracts/utils/cryptography/SIP712.sol&quot;;

import &quot;./ISRC5453.sol&quot;;

abstract contract ASRC5453Endorsible is SIP712,
    ISRC5453EndorsementCore, ISRC5453EndorsementDigest, ISRC5453EndorsementDataTypeA, ISRC5453EndorsementDataTypeB {
    // ...

    function _validate(
        bytes32 msgDigest,
        SingleEndorsementData memory endersement
    ) internal virtual {
        require(
            endersement.sig.length == 65,
            &quot;ASRC5453Endorsible: wrong signature length&quot;
        );
        require(
            SignatureChecker.isValidSignatureNow(
                endersement.endorserAddress,
                msgDigest,
                endersement.sig
            ),
            &quot;ASRC5453Endorsible: invalid signature&quot;
        );
    }
    // ...

    modifier onlyEndorsed(
        bytes32 _functionParamStructHash,
        bytes calldata _extensionData
    ) {
        require(_isEndorsed(_functionParamStructHash, _extensionData));
        _;
    }

    function computeExtensionDataTypeB(
        uint256 nonce,
        uint256 validSince,
        uint256 validBy,
        address[] calldata endorserAddress,
        bytes[] calldata sigs
    ) external pure override returns (bytes memory) {
        require(endorserAddress.length == sigs.length);
        SingleEndorsementData[]
            memory endorsements = new SingleEndorsementData[](
                endorserAddress.length
            );
        for (uint256 i = 0; i &lt; endorserAddress.length; ++i) {
            endorsements[i] = SingleEndorsementData(
                endorserAddress[i],
                sigs[i]
            );
        }
        return
            abi.encode(
                GeneralExtensionDataStruct(
                    MAGIC_WORLD,
                    SRC5453_TYPE_B,
                    nonce,
                    validSince,
                    validBy,
                    abi.encode(endorsements)
                )
            );
    }
}

```

See [`ASRC5453.sol`](/assets/sip-5453/ASRC5453.sol)

### Reference Implementation of `EndorsableSRC721`

Here is a reference implementation of `EndorsableSRC721` that achieves similar behavior to [SRC-4494](/SIPS/sip-4494).

```solidity
pragma solidity ^0.8.9;

contract EndorsableSRC721 is SRC721, ASRC5453Endorsible {
    //...

    function mint(
        address _to,
        uint256 _tokenId,
        bytes calldata _extraData
    )
        external
        onlyEndorsed(
            _computeFunctionParamHash(
                &quot;function mint(address _to,uint256 _tokenId)&quot;,
                abi.encode(_to, _tokenId)
            ),
            _extraData
        )
    {
        _mint(_to, _tokenId);
    }
}
```

See [`EndorsableSRC721.sol`](/assets/sip-5453/EndorsableSRC721.sol)

### Reference Implementation of `ThresholdMultiSigForwarder`

Here is a reference implementation of ThresholdMultiSigForwarder that achieves similar behavior of multi-sig threshold approval
remote contract call like a Gnosis-Safe wallet.

```solidity
pragma solidity ^0.8.9;

contract ThresholdMultiSigForwarder is ASRC5453Endorsible {
    //...
    function forward(
        address _dest,
        uint256 _value,
        uint256 _gasLimit,
        bytes calldata _calldata,
        bytes calldata _extraData
    )
        external
        onlyEndorsed(
            _computeFunctionParamHash(
                &quot;function forward(address _dest,uint256 _value,uint256 _gasLimit,bytes calldata _calldata)&quot;,
                abi.encode(_dest, _value, _gasLimit, keccak256(_calldata))
            ),
            _extraData
        )
    {
        string memory errorMessage = &quot;Fail to call remote contract&quot;;
        (bool success, bytes memory returndata) = _dest.call{value: _value}(
            _calldata
        );
        Address.verifyCallResult(success, returndata, errorMessage);
    }

}

```

See [`ThresholdMultiSigForwarder.sol`](/assets/sip-5453/ThresholdMultiSigForwarder.sol)

## Security Considerations

### Replay Attacks

A replay attack is a type of attack on cryptography authentication. In a narrow sense, it usually refers to a type of attack that circumvents the cryptographically signature verification by reusing an existing signature for a message being signed again. Any implementations relying on this SIP must realize that all smart endorsements described here are cryptographic signatures that are _public_ and can be obtained by anyone. They must foresee the possibility of a replay of the transactions not only at the exact deployment of the same smart contract, but also other deployments of similar smart contracts, or of a version of the same contract on another `chainId`, or any other similar attack surfaces. The `nonce`, `validSince`, and `validBy` fields are meant to restrict the surface of attack but might not fully eliminate the risk of all such attacks, e.g. see the [Phishing](#phishing) section.

### Phishing

It&apos;s worth pointing out a special form of replay attack by phishing. An adversary can design another smart contract in a way that the user may be tricked into signing a smart endorsement for a seemingly legitimate purpose, but the data-to-designed matches the target application

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Fri, 12 Aug 2022 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-5453</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-5453</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Multi-privilege Management NFT Extension</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #5496 - Multi-privilege Management NFT Extension&lt;/strong&gt; is in Last Call status. It is authored by Jeremy Z (@wnft) and was originally created 2022-07-30. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-5496-multi-privilege-management-extension-for-src-721/10427&quot;&gt;https://sila-magicians.org/t/sip-5496-multi-privilege-management-extension-for-src-721/10427&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP defines an interface extending [SIP-721](/SIPS/sip-721) to provide shareable multi-privileges for NFTs. Privileges may be on-chain (voting rights, permission to claim an airdrop) or off-chain (a coupon for an online store, a discount at a local restaurant, access to VIP lounges in airports). Each NFT may contain many privileges, and the holder of a privilege can verifiably transfer that privilege to others. Privileges may be non-shareable or shareable. Shareable privileges can be cloned, with the provider able to adjust the details according to the spreading path. Expiration periods can also be set for each privilege.

## Motivation

This standard aims to efficiently manage privileges attached to NFTs in real-time. Many NFTs have functions other than just being used as profile pictures or art collections, they may have real utilities in different scenarios. For example, a fashion store may give a discount for its own NFT holders; a DAO member NFT holder can vote for the proposal of how to use their treasury; a dApp may create an airdrop event to attract a certain group of people like some blue chip NFT holders to claim; the grocery store can issue its membership card on chain (as an NFT) and give certain privileges when the members shop at grocery stores, etc. There are cases when people who own NFTs do not necessarily want to use their privileges. By providing additional data recording different privileges a NFT collection has and interfaces to manage them, users can transfer or sell privileges without losing their ownership of the NFT.

[SIP-721](/SIPS/sip-721) only records the ownership and its transfer, the privileges of an NFT are not recorded on-chain. This extension would allow merchants/projects to give out a certain privilege to a specified group of people, and owners of the privileges can manage each one of the privileges independently. This facilitates a great possibility for NFTs to have real usefulness.

For example, an airline company issues a series of [SIP-721](/SIPS/sip-721)/[SIP-1155](/SIPS/sip-1155) tokens to Crypto Punk holders to give them privileges, in order to attract them to join their club. However, since these tokens are not bound to the original NFT, if the original NFT is transferred, these privileges remain in the hands of the original holders, and the new holders cannot enjoy the privileges automatically.
So, we propose a set of interfaces that can bind the privileges to the underlying NFT, while allowing users to manage the privileges independently.

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Every contract complying with this standard MUST implement the `ISRC5496` interface. The **shareable multi-privilege extension** is OPTIONAL for SIP-721 contracts.

```solidity
/// @title multi-privilege extension for SIP-721
///  Note: the SIP-165 identifier for this interface is 0x076e1bbb
interface ISRC5496{
    /// @notice Emitted when `owner` changes the `privilege holder` of a NFT.
    event PrivilegeAssigned(uint256 tokenId, uint256 privilegeId, address user, uint256 expires);
    /// @notice Emitted when `contract owner` changes the `total privilege` of the collection
    event PrivilegeTotalChanged(uint256 newTotal, uint256 oldTotal);

    /// @notice set the privilege holder of a NFT.
    /// @dev expires should be less than 30 days
    /// Throws if `msg.sender` is not approved or owner of the tokenId.
    /// @param tokenId The NFT to set privilege for
    /// @param privilegeId The privilege to set
    /// @param user The privilege holder to set
    /// @param expires For how long the privilege holder can have
    function setPrivilege(uint256 tokenId, uint256 privilegeId, address user, uint256 expires) external;

    /// @notice Return the expiry timestamp of a privilege
    /// @param tokenId The identifier of the queried NFT
    /// @param privilegeId The identifier of the queried privilege
    /// @return Whether a user has a certain privilege
    function privilegeExpires(uint256 tokenId, uint256 privilegeId) external view returns(uint256);

    /// @notice Check if a user has a certain privilege
    /// @param tokenId The identifier of the queried NFT
    /// @param privilegeId The identifier of the queried privilege
    /// @param user The address of the queried user
    /// @return Whether a user has a certain privilege
    function hasPrivilege(uint256 tokenId, uint256 privilegeId, address user) external view returns(bool);
}
```

Every contract implementing this standard SHOULD set a maximum privilege number before setting any privilege, the `privilegeId` MUST NOT be greater than the maximum privilege number.

The `PrivilegeAssigned` event MUST be emitted when `setPrivilege` is called.

The `PrivilegeTotalChanged` event MUST be emitted when the `total privilege` of the collection is changed.

The `supportsInterface` method MUST return `true` when called with `0x076e1bbb`.

```solidity
/// @title Cloneable extension - Optional for SIP-721
interface ISRC721Cloneable {
    /// @notice Emitted when set the `privilege ` of a NFT cloneable.
    event PrivilegeCloned(uint tokenId, uint privId, address from, address to);

    /// @notice set a certain privilege cloneable
    /// @param tokenId The identifier of the queried NFT
    /// @param privilegeId The identifier of the queried privilege
    /// @param referrer The address of the referrer
    /// @return Whether the operation is successful or not
    function clonePrivilege(uint tokenId, uint privId, address referrer) external returns (bool);
}
```

The `PrivilegeCloned` event MUST be emitted when `clonePrivilege` is called.

For Compliant contract, it is RECOMMENDED to use [SIP-1271](/SIPS/sip-1271) to validate the signatures.

## Rationale

### Shareable Privileges

The number of privilege holders is limited by the number of NFTs if privileges are non-shareable. A shareable privilege means the original privilege holder can copy the privilege and give it to others, not transferring his/her own privilege to them. This mechanism greatly enhances the spread of privileges as well as the adoption of NFTs.

### Expire Date Type

The expiry timestamp of a privilege is a timestamp and stored in `uint256` typed variables.

### Beneficiary of Referrer

For example, a local pizza shop offers a 30% off Coupon and the owner of the shop encourages their consumers to share the coupon with friends, then the friends can get the coupon. Let&apos;s say Tom gets 30% off Coupon from the shop and he shares the coupon with Alice. Alice gets the coupon too and Alice&apos;s referrer is Tom. For some certain cases, Tom may get more rewards from the shop. This will help the merchants in spreading the promotion among consumers.

### Proposal: NFT Transfer

If the owner of the NFT transfers ownership to another user, there is no impact on &quot;privileges&quot;. But errors may occur if the owner tries to withdraw the original [SIP-721](/SIPS/sip-721) token from the wrapped NFT through `unwrap()` if any available privileges are still ongoing. We protect the rights of holders of the privileges to check the last expiration date of the privilege.

```solidity
function unwrap(uint256 tokenId, address to) external {
    require(getBlockTimestamp() &gt;= privilegeBook[tokenId].lastExpiresAt, &quot;privilege not yet expired&quot;);

    require(ownerOf(tokenId) == msg.sender, &quot;not owner&quot;);

    _burn(tokenId);

    ISRC721(nft).transferFrom(address(this), to, tokenId);

    emit Unwrap(nft, tokenId, msg.sender, to);
}
```

## Backwards Compatibility

This SIP is compatible with any kind of NFTs that follow the SIP-721 standard. It only adds more functions and data structures without interfering with the original [SIP-721](/SIPS/sip-721) standard.

## Test Cases

Test cases are implemented with the reference implementation.

### Test Code

[test.js](/assets/sip-5496/test/test.js)

Run in terminal:

```shell
truffle test ./test/test.js
```

[testCloneable.js](/assets/sip-5496/test/testCloneable.js)

Run in terminal:

```shell
truffle test ./test/testCloneable.js
```

## Reference Implementation

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0; 

import &quot;@openzeppelin/contracts/token/SRC721/SRC721.sol&quot;;
import &quot;@openzeppelin/contracts/utils/introspection/ISRC165.sol&quot;;
import &quot;./ISRC5496.sol&quot;;

contract SRC5496 is SRC721, ISRC5496 {
    struct PrivilegeRecord {
        address user;
        uint256 expiresAt;
    }
    struct PrivilegeStorage {
        uint lastExpiresAt;
        // privId =&gt; PrivilegeRecord
        mapping(uint =&gt; PrivilegeRecord) privilegeEntry;
    }

    uint public privilegeTotal;
    // tokenId =&gt; PrivilegeStorage
    mapping(uint =&gt; PrivilegeStorage) public privilegeBook;
    mapping(address =&gt; mapping(address =&gt; bool)) private privilegeDelegator;

    constructor(string memory name_, string memory symbol_)
    SRC721(name_,symbol_)
    {
    
    }

    function setPrivilege(
        uint tokenId,
        uint privId,
        address user,
        uint64 expires
    ) external virtual {
        require((hasPrivilege(tokenId, privId, ownerOf(tokenId)) &amp;&amp; _isApprovedOrOwner(msg.sender, tokenId)) || _isDelegatorOrHolder(msg.sender, tokenId, privId), &quot;SRC721: transfer caller is not owner nor approved&quot;);
        require(expires &lt; block.timestamp + 30 days, &quot;expire time invalid&quot;);
        require(privId &lt; privilegeTotal, &quot;invalid privilege id&quot;);
        privilegeBook[tokenId].privilegeEntry[privId].user = user;
        if (_isApprovedOrOwner(msg.sender, tokenId)) {
            privilegeBook[tokenId].privilegeEntry[privId].expiresAt = expires;
            if (privilegeBook[tokenId].lastExpiresAt &lt; expires) {
                privilegeBook[tokenId].lastExpiresAt = expires;
            }
        }
        emit PrivilegeAssigned(tokenId, privId, user, uint64(privilegeBook[tokenId].privilegeEntry[privId].expiresAt));
    }

    function hasPrivilege(
        uint256 tokenId,
        uint256 privId,
        address user
    ) public virtual view returns(bool) {
        if (privilegeBook[tokenId].privilegeEntry[privId].expiresAt &gt;= block.timestamp){
            return privilegeBook[tokenId].privilegeEntry[privId].user == user;
        }
        return ownerOf(tokenId) == user;
    }

    function privilegeExpires(
        uint256 tokenId,
        uint256 privId
    ) public virtual view returns(uint256){
        return privilegeBook[tokenId].privilegeEntry[privId].expiresAt;
    }

    function _setPrivilegeTotal(
        uint total
    ) internal {
        emit PrivilegeTotalChanged(total, privilegeTotal);
        privilegeTotal = total;
    }

    function getPrivilegeInfo(uint tokenId, uint privId) external view returns(address user, uint256 expiresAt) {
        return (privilegeBook[tokenId].privilegeEntry[privId].user, privilegeBook[tokenId].privilegeEntry[privId].expiresAt);
    }

    function setDelegator(address delegator, bool enabled) external {
        privilegeDelegator[msg.sender][delegator] = enabled;
    }

    function _isDelegatorOrHolder(address delegator, uint256 tokenId, uint privId) internal virtual view returns (bool) {
        address holder = privilegeBook[tokenId].privilegeEntry[privId].user;
         return (delegator == holder || isApprovedForAll(holder, delegator) || privilegeDelegator[holder][delegator]);
    }

    function supportsInterface(bytes4 interfaceId) public override virtual view returns (bool) {
        return interfaceId == type(ISRC5496).interfaceId || super.supportsInterface(interfaceId);
    }
}
```

## Security Considerations

Implementations must thoroughly consider who has the permission to set or clone privileges.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Sat, 30 Jul 2022 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-5496</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-5496</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Contracts Dependencies Registry</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #6224 - Contracts Dependencies Registry&lt;/strong&gt; is in Last Call status. It is authored by Artem Chystiakov (@arvolear) and was originally created 2022-12-27. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-6224-contracts-dependencies-registry/12316&quot;&gt;https://sila-magicians.org/t/sip-6224-contracts-dependencies-registry/12316&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP introduces an on-chain registry system that a decentralized protocol may use to manage its smart contracts.

The proposed system consists of two components: `ContractsRegistry` and `Dependant`. The `ContractsRegistry` contract stores references to every smart contract used within a protocol, optionally making them upgradeable by deploying self-managed proxies on top, and acts as a hub the `Dependant` contracts query to fetch their required dependencies from.

## Motivation

In the ever-growing Sila ecosystem, projects tend to become more and more complex. Modern protocols require portability and agility to satisfy customer needs by continuously delivering new features and staying on pace with the industry. However, the requirement is hard to achieve due to the immutable nature of blockchains and smart contracts. Moreover, the increased complexity and continuous delivery bring bugs and entangle the dependencies between the contracts, making systems less supportable.

Applications that have a clear architectural facade; which are designed with forward compatibility in mind; which dependencies are transparent and clean are easier to develop and maintain. The given SIP tries to solve the aforementioned problems by presenting two smart contracts: the `ContractsRegistry` and the `Dependant`.

The advantages of using the provided system might be:

- Structured smart contracts management via specialized contracts.
- Ad-hoc upgradeability provision of a protocol.
- Runtime addition, removal, and substitution of smart contracts.
- Dependency injection mechanism to keep smart contracts&apos; dependencies under control.
- Ability to specify custom access control rules to maintain the protocol.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

### Overview

The system consists of two smart contracts:

- `ContractsRegistry` that is a singleton registry to manage and upgrade a protocol&apos;s smart contracts.
- `Dependant` that is a mix-in which enables a dependency injection mechanism.

The following diagram depicts the relationship between the registry and its dependants:

![](/assets/sip-6224/diagram.svg)

### ContractsRegistry

The `ContractsRegistry` is the main contract of the proposed system. It MUST store the references to every standalone contract used within a protocol. The `ContractRegistry` MAY be configured to deploy a proxy contract of choice on top of the registered contracts. 

Additionally, the `ContractsRegistry` MUST reject the registration of zero addresses.

The `ContractsRegistry` MUST implement the following interface:

```solidity
pragma solidity ^0.8.0;

interface IContractsRegistry {
    /**
     * @notice The event that is emitted when the contract gets added to the registry
     * @param name the name of the contract
     * @param contractAddress the address of the added contract
     */
    event ContractAdded(string name, address contractAddress);
 
    /**
     * @notice The event that is emitted when the proxy contract gets added to the registry
     * @param name the name of the contract
     * @param contractAddress the address of the proxy contract
     * @param implementation the address of the implementation contract
     */
    event ProxyContractAdded(string name, address contractAddress, address implementation);
 
    /**
     * @notice The event that is emitted when the proxy contract gets upgraded through the registry
     * @param name the name of the contract
     * @param newImplementation the address of the new implementation contract
     */
    event ProxyContractUpgraded(string name, address newImplementation);
 
    /**
     * @notice The event that is emitted when the contract gets removed from the registry
     * @param name the name of the removed contract
     */
    event ContractRemoved(string name);
 
    /**
     * @notice The function that returns an associated contract by the name. 
     *
     * MUST revert if the requested contract is `address(0)`
     *
     * @param name the name of the contract
     * @return the address of the contract
     */
    function getContract(string memory name) external view returns (address);
 
    /**
     * @notice The function that checks if a contract with a given name has been added
     * @param name the name of the contract
     * @return true if the contract is present in the registry
     */
    function hasContract(string memory name) external view returns (bool);
 
    /**
     * @notice The function that injects dependencies into the given contract.
     *
     * MUST call the `setDependencies()` with `address(this)` and `bytes(&quot;&quot;)` as arguments on the provided contract
     *
     * @param name the name of the contract
     */
    function injectDependencies(string memory name) external;
 
    /**
     * @notice The function that injects dependencies into the given contract with extra data.
     *
     * MUST call the `setDependencies()` with `address(this)` and `data` as arguments on the provided contract
     *
     * @param name the name of the contract
     * @param data the extra context data that will be passed to the dependant contract
     */
    function injectDependenciesWithData(
        string memory name,
        bytes memory data
    ) external;
 
    /**
     * @notice The function that upgrades added proxy contract with a new implementation.
     *
     * It is the Owner&apos;s responsibility to ensure the compatibility between implementations.
     *
     * MUST emit `ProxyContractUpgraded` event
     *
     * @param name the name of the proxy contract
     * @param newImplementation the new implementation the proxy will be upgraded to
     */
    function upgradeContract(string memory name, address newImplementation) external;
 
    /**
     * @notice The function that upgrades added proxy contract with a new implementation, providing data
     *
     * It is the Owner&apos;s responsibility to ensure the compatibility between implementations.
     *
     * MUST emit `ProxyContractUpgraded` event
     *
     * @param name the name of the proxy contract
     * @param newImplementation the new implementation the proxy will be upgraded to
     * @param data the data that the proxy will be called with after upgrade. This can be an ABI encoded function call
     */
    function upgradeContractAndCall(
        string memory name,
        address newImplementation,
        bytes memory data
    ) external;
 
    /**
     * @notice The function that adds pure (non-proxy) contracts to the `ContractsRegistry`. The contracts MAY either be
     * the ones the system does not have direct upgradeability control over or those that are not upgradeable by design.
     *
     * MUST emit `ContractAdded` event. Reverts if the provided address is `address(0)`
     *
     * @param name the name to associate the contract with
     * @param contractAddress the address of the contract to be added
     */
    function addContract(string memory name, address contractAddress) external;
 
    /**
     * @notice The function that adds the proxy contracts to the registry by deploying them above the provided implementation.
     *
     * The function may be used to add a contract that the `ContractsRegistry` has to be able to upgrade.
     *
     * MUST emit `ProxyContractAdded` event. Reverts if implementation address is `address(0)`
     *
     * @param name the name to associate the contract with
     * @param contractAddress the address of the implementation to point the proxy to
     */
    function addProxyContract(string memory name, address contractAddress) external;
 
    /**
     * @notice The function that adds the proxy contracts to the registry by deploying them above the provided implementation,
     * providing data.
     *
     * The function may be used to add a contract that the `ContractsRegistry` has to be able to upgrade.
     *
     * MUST emit `ProxyContractAdded` event. Reverts if implementation address is `address(0)`
     *
     * @param name the name to associate the contract with
     * @param contractAddress the address of the implementation
     * @param data the data that the proxy will be called with. This can be an ABI encoded initialization call
     */
    function addProxyContractAndCall(
        string memory name,
        address contractAddress,
        bytes memory data
    ) external;
 
    /**
     * @notice The function that adds an already deployed proxy to the `ContractsRegistry`. It MAY be used
     * when the system migrates to the new `ContractRegistry`. In that case, the new registry MUST have the
     * credentials to upgrade the newly added proxies.
     *
     * MUST emit `ProxyContractAdded` event. Reverts if implementation address is `address(0)`
     *
     * @param name the name to associate the contract with
     * @param contractAddress the address of the proxy
     */
    function justAddProxyContract(string memory name, address contractAddress) external;
 
    /**
     * @notice The function to remove contracts from the ContractsRegistry.
     *
     * MUST emit `ContractRemoved` event. Reverts if the contract is already removed
     *
     * @param name the associated name with the contract
     */
    function removeContract(string memory name) external;
}
```

### Dependant

The `ContractsRegistry` works together with the `Dependant` contract. Every standalone contract of a protocol MUST inherit `Dependant` in order to support the dependency injection mechanism. 

The required dependencies MUST be set in the overridden `setDependencies` method, not in the `constructor` or `initializer` methods.

Only the injector MUST be able to call the `setDependencies` and `setInjector` methods. The initial injector will be a zero address, in that case, the call MUST NOT revert on access control checks.

The `Dependant` contract MUST implement the following interface:

```solidity
pragma solidity ^0.8.0;

interface IDependant {
    /**
     * @notice The function that is called from the `ContractsRegistry` to inject dependencies.
     *
     * The contract MUST perform a proper access check of `msg.sender`. The calls should only be possible from `ContractsRegistry`
     *
     * @param contractsRegistry the registry to pull dependencies from
     * @param data the extra data that might provide additional application-specific context
     */
    function setDependencies(address contractsRegistry, bytes memory data) external;
 
    /**
     * @notice The function that sets the new dependency injector.
     *
     * The contract MUST perform a proper access check of `msg.sender`
     *
     * @param injector the new dependency injector
     */
    function setInjector(address injector) external;
 
    /**
     * @notice The function that gets the current dependency injector
     * @return the current dependency injector
     */
    function getInjector() external view returns (address);
}
```

- The `Dependant` contract MAY store the dependency injector (usually `ContractsRegistry`) address in the special slot `0x3d1f25f1ac447e55e7fec744471c4dab1c6a2b6ffb897825f9ea3d2e8c9be583` (obtained as `bytes32(uint256(keccak256(&quot;sip6224.dependant.slot&quot;)) - 1)`).

## Rationale

There are a few design decisions that have to be explicitly specified:

### ContractsRegistry Rationale

#### Contracts Identifier

The `string` contracts identifier is chosen over the `uint256` and `bytes32` to maintain code readability and reduce the human error chances when interacting with the `ContractsRegistry`. Being the topmost smart contract of a protocol, it MAY be typical for the users to interact with it via block explorers or DAOs. Clarity was prioritized over gas usage.

Due to the `string` identifier, the event parameters are not indexed. The `string indexed` parameter will become the `keccak256` hash of the contract name if it is larger than 32 bytes. This fact reduces readability, which was prioritized.

#### Reverts

The `getContract` view function reverts if the requested contract is `address(0)`. This is essential to minimize the risks of misinitialization of a protocol. Correct contracts SHOULD be added to the registry prior to any dependency injection actions.

The `addContract`, `addProxyContract`, `addProxyContractAndCall`, and `justAddProxyContract` methods revert if the provided address is `address(0)` for the same risk minimization reason.

### Dependant Rationale

#### Dependencies

The `data` parameter is provided to carry additional application-specific context. It MAY be used to extend the method&apos;s behavior.

#### Injector

The `setInjector` function is made `external` to support the dependency injection mechanism for factory-made contracts. However, the method SHOULD be used with extra care.

## Reference Implementation

&gt; Note that the reference implementation depends on OpenZeppelin contracts `4.9.2`.

### ContractsRegistry Implementation

```solidity
pragma solidity ^0.8.0;

import {Address} from &quot;@openzeppelin/contracts/utils/Address.sol&quot;;
import {TransparentUpgradeableProxy} from &quot;@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol&quot;;
import {OwnableUpgradeable} from &quot;@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol&quot;;

import {Dependant} from &quot;./Dependant.sol&quot;;

interface IContractsRegistry {
    event ContractAdded(string name, address contractAddress);
    event ProxyContractAdded(
        string name,
        address contractAddress,
        address implementation
    );
    event ProxyContractUpgraded(string name, address newImplementation);
    event ContractRemoved(string name);

    function getContract(string memory name) external view returns (address);

    function hasContract(string memory name) external view returns (bool);

    function injectDependencies(string memory name) external;

    function injectDependenciesWithData(string memory name, bytes memory data)
        external;

    function upgradeContract(string memory name, address newImplementation)
        external;

    function upgradeContractAndCall(
        string memory name,
        address newImplementation,
        bytes memory data
    ) external;

    function addContract(string memory name, address contractAddress) external;

    function addProxyContract(string memory name, address contractAddress)
        external;

    function addProxyContractAndCall(
        string memory name,
        address contractAddress,
        bytes memory data
    ) external;

    function justAddProxyContract(string memory name, address contractAddress)
        external;

    function removeContract(string memory name) external;
}

contract ProxyUpgrader {
    using Address for address;

    address private immutable _OWNER;

    modifier onlyOwner() {
        _onlyOwner();
        _;
    }

    constructor() {
        _OWNER = msg.sender;
    }

    function upgrade(address what_, address to_, bytes calldata data_) external onlyOwner {
        if (data_.length &gt; 0) {
            TransparentUpgradeableProxy(payable(what_)).upgradeToAndCall(to_, data_);
        } else {
            TransparentUpgradeableProxy(payable(what_)).upgradeTo(to_);
        }
    }

    function getImplementation(address what_) external view onlyOwner returns (address) {
        // bytes4(keccak256(&quot;implementation()&quot;)) == 0x5c60da1b
        (bool success_, bytes memory returndata_) = address(what_).staticcall(hex&quot;5c60da1b&quot;);

        require(success_, &quot;ProxyUpgrader: not a proxy&quot;);

        return abi.decode(returndata_, (address));
    }

    function _onlyOwner() internal view {
        require(_OWNER == msg.sender, &quot;ProxyUpgrader: not an owner&quot;);
    }
}

contract ContractsRegistry is IContractsRegistry, OwnableUpgradeable {
    ProxyUpgrader private _proxyUpgrader;

    mapping(string =&gt; address) private _contracts;
    mapping(address =&gt; bool) private _isProxy;

    function __ContractsRegistry_init() public initializer {
        _proxyUpgrader = new ProxyUpgrader();

        __Ownable_init();
    }

    function getContract(string memory name_) public view returns (address) {
        address contractAddress_ = _contracts[name_];

        require(
            contractAddress_ != address(0),
            &quot;ContractsRegistry: this mapping doesn&apos;t exist&quot;
        );

        return contractAddress_;
    }

    function hasContract(string memory name_) public view returns (bool) {
        return _contracts[name_] != address(0);
    }

    function getProxyUpgrader() external view returns (address) {
        return address(_proxyUpgrader);
    }

    function injectDependencies(string memory name_) public virtual onlyOwner {
        injectDependenciesWithData(name_, bytes(&quot;&quot;));
    }

    function injectDependenciesWithData(string memory name_, bytes memory data_)
        public
        virtual
        onlyOwner
    {
        address contractAddress_ = _contracts[name_];

        require(
            contractAddress_ != address(0),
            &quot;ContractsRegistry: this mapping doesn&apos;t exist&quot;
        );

        Dependant dependant_ = Dependant(contractAddress_);
        dependant_.setDependencies(address(this), data_);
    }

    function upgradeContract(string memory name_, address newImplementation_)
        public
        virtual
        onlyOwner
    {
        upgradeContractAndCall(name_, newImplementation_, bytes(&quot;&quot;));
    }

    function upgradeContractAndCall(
        string memory name_,
        address newImplementation_,
        bytes memory data_
    ) public virtual onlyOwner {
        address contractToUpgrade_ = _contracts[name_];

        require(
            contractToUpgrade_ != address(0),
            &quot;ContractsRegistry: this mapping doesn&apos;t exist&quot;
        );
        require(
            _isProxy[contractToUpgrade_],
            &quot;ContractsRegistry: not a proxy contract&quot;
        );

        _proxyUpgrader.upgrade(contractToUpgrade_, newImplementation_, data_);

        emit ProxyContractUpgraded(name_, newImplementation_);
    }

    function addContract(string memory name_, address contractAddress_)
        public
        virtual
        onlyOwner
    {
        require(
            contractAddress_ != address(0),
            &quot;ContractsRegistry: zero address is forbidden&quot;
        );

        _contracts[name_] = contractAddress_;

        emit ContractAdded(name_, contractAddress_);
    }

    function addProxyContract(string memory name_, address contractAddress_)
        public
        virtual
        onlyOwner
    {
        addProxyContractAndCall(name_, contractAddress_, bytes(&quot;&quot;));
    }

    function addProxyContractAndCall(
        string memory name_,
        address contractAddress_,
        bytes memory data_
    ) public virtual onlyOwner {
        require(
            contractAddress_ != address(0),
            &quot;ContractsRegistry: zero address is forbidden&quot;
        );

        address proxyAddr_ = _deployProxy(
            contractAddress_,
            address(_proxyUpgrader),
            data_
        );

        _contracts[name_] = proxyAddr_;
        _isProxy[proxyAddr_] = true;

        emit ProxyContractAdded(name_, proxyAddr_, contractAddress_);
    }

    function justAddProxyContract(string memory name_, address contractAddress_)
        public
        virtual
        onlyOwner
    {
        require(
            contractAddress_ != address(0),
            &quot;ContractsRegistry: zero address is forbidden&quot;
        );

        _contracts[name_] = contractAddress_;
        _isProxy[contractAddress_] = true;

        emit ProxyContractAdded(
            name_,
            contractAddress_,
            _proxyUpgrader.getImplementation(contractAddress_)
        );
    }

    function removeContract(string memory name_) public virtual onlyOwner {
        address contractAddress_ = _contracts[name_];

        require(
            contractAddress_ != address(0),
            &quot;ContractsRegistry: this mapping doesn&apos;t exist&quot;
        );

        delete _isProxy[contractAddress_];
        delete _contracts[name_];

        emit ContractRemoved(name_);
    }

    function _deployProxy(
        address contractAddress_,
        address admin_,
        bytes memory data_
    ) internal virtual returns (address) {
        return
            address(
                new TransparentUpgradeableProxy(contractAddress_, admin_, data_)
            );
    }
}
```

### Dependant Implementation

```solidity
pragma solidity ^0.8.0;

interface IDependant {
    function setDependencies(address contractsRegistry, bytes memory data) external;
 
    function setInjector(address injector) external;
 
    function getInjector() external view returns (address);
}

abstract contract Dependant is IDependant {
    /**
     * @dev bytes32(uint256(keccak256(&quot;sip6224.dependant.slot&quot;)) - 1)
     */
    bytes32 private constant _INJECTOR_SLOT =
        0x3d1f25f1ac447e55e7fec744471c4dab1c6a2b6ffb897825f9ea3d2e8c9be583;

    modifier dependant() {
        _checkInjector();
        _;
        _setInjector(msg.sender);
    }

    function setDependencies(address contractsRegistry_, bytes memory data_) public virtual;

    function setInjector(address injector_) external {
        _checkInjector();
        _setInjector(injector_);
    }

    function getInjector() public view returns (address injector_) {
        bytes32 slot_ = _INJECTOR_SLOT;

        assembly {
            injector_ := sload(slot_)
        }
    }

    function _setInjector(address injector_) internal {
        bytes32 slot_ = _INJECTOR_SLOT;

        assembly {
            sstore(slot_, injector_)
        }
    }

    function _checkInjector() internal view {
        address injector_ = getInjector();

        require(injector_ == address(0) || injector_ == msg.sender, &quot;Dependant: not an injector&quot;);
    }
}
```

## Security Considerations

It is crucial for the owner of `ContractsRegistry` to keep their keys in a safe place. The loss/leakage of credentials to the `ContractsRegistry` will lead to the application&apos;s point of no return. The `ContractRegistry` is a cornerstone of a protocol, access must be granted to the trusted parties only.

### ContractsRegistry Security

- The `ContractsRegistry` does not perform any upgradeability checks between the proxy upgrades. It is the user&apos;s responsibility to make sure that the new implementation is compatible with the old one.

### Dependant Security

- The `Dependant` contract MUST set its dependency injector no later than the first call to the `setDependencies` function is made. That being said, it is possible to front-run the first dependency injection.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Tue, 27 Dec 2022 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-6224</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-6224</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Single-contract Multi-delegatecall</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #6357 - Single-contract Multi-delegatecall&lt;/strong&gt; is in Last Call status. It is authored by Gavin John (@Pandapip1) and was originally created 2023-01-18. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-6357-single-contract-multicall/12621&quot;&gt;https://sila-magicians.org/t/sip-6357-single-contract-multicall/12621&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP standardizes an interface containing a single function, `multicall`, allowing EOAs to call multiple functions of a smart contract in a single transaction, and revert all calls if any call fails. 

## Motivation

Currently, in order to transfer several [SRC-721](/SIPS/sip-721) NFTs, one needs to submit a number of transactions equal to the number of NFTs being tranferred. This wastes users&apos; funds by requiring them to pay 21000 gas fee for every NFT they transfer.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Contracts implementing this SIP must implement the following interface:
  
```solidity
pragma solidity ^0.8.0;

interface IMulticall {
    /// @notice           Takes an array of abi-encoded call data, delegatecalls itself with each calldata, and returns the abi-encoded result
    /// @dev              Reverts if any delegatecall reverts
    /// @param    data    The abi-encoded data
    /// @returns  results The abi-encoded return values
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results);

    /// @notice           OPTIONAL. Takes an array of abi-encoded call data, delegatecalls itself with each calldata, and returns the abi-encoded result
    /// @dev              Reverts if any delegatecall reverts
    /// @param    data    The abi-encoded data
    /// @param    values  The effective msg.values. These must add up to at most msg.value
    /// @returns  results The abi-encoded return values
    function multicallPayable(bytes[] calldata data, uint256[] values) external payable virtual returns (bytes[] memory results);
}
```

## Rationale

`multicallPayable` is optional because it isn&apos;t always feasible to implement, due to the `msg.value` splitting.

## Backwards Compatibility

This is compatible with most existing multicall functions.

## Test Cases

The following JavaScript code, using the Ethers library, should atomically transfer `amt` units of an [SRC-20](/SIPS/sip-20) token to both `addressA` and `addressB`.

```js
await token.multicall(await Promise.all([
    token.interface.encodeFunctionData(&apos;transfer&apos;, [ addressA, amt ]),
    token.interface.encodeFunctionData(&apos;transfer&apos;, [ addressB, amt ]),
]));
```

## Reference Implementation

```solidity
pragma solidity ^0.8.0;

/// Derived from OpenZeppelin&apos;s implementation
abstract contract Multicall is IMulticall {
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i &lt; data.length; i++) {
            (bool success, bytes memory returndata) = address(this).delegatecall(data[i]);
            require(success);
            results[i] = returndata;
        }
        return results;
    }
}
```

## Security Considerations

`multicallPayable` should only be used if the contract is able to support it. A naive attempt at implementing it could allow an attacker to call a payable function multiple times with the same sila.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Wed, 18 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-6357</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-6357</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Empty accounts deprecation</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #7523 - Empty accounts deprecation&lt;/strong&gt; is in Last Call status. It is authored by Peter Davies (@petertdavies) and was originally created 2023-09-19. It is in the Core category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-7523-empty-accounts-deprecation/15870&quot;&gt;https://sila-magicians.org/t/sip-7523-empty-accounts-deprecation/15870&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP prohibits the state of any post-merge network from containing empty accounts. Since no empty accounts exist outside the testsuite and no new ones can be created this requirement is already achieved in practice. An explicit ban reduces technical debt going forward.

## Motivation

The possibility of empty accounts is a historical artifact of the early history of Sila. The only networks that have ever been capable of containing them are Sila SilaMainnet, the deprecated testnet Ropsten, Etheruem Classic SilaMainnet and various Sila Classic testnets. All remaining empty accounts on SilaMainnet were cleared in block `14049881` (transaction `0xf955834bfa097458a9cf6b719705a443d32e7f43f20b9b0294098c205b4bcc3d`) and a similar transaction was sent on Sila Classic. None of the other myriad SVM-compatible networks are old enough to have empty accounts and there is no realistic prospect that anyone will encounter an empty account in a production context.

Despite empty accounts no longer existing, they still impose a legacy of technical debt. [SIP-161](/SIPS/sip-161) imposes complicated rules that require a client to delete an empty account when it is &quot;touched&quot;. As the Sila specification continues to evolve new edgecases of the &quot;touch&quot; rules arise which must be debated, implemented, tested and documented. If a future client wishes to only support post-merge blocks it must implement unnecessary empty account support solely to pass the test suite.

By prohibiting empty accounts on post-merge networks, this SIP frees designers and implementers of Sila and related blockchains from the burden of having to consider them going forward.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

An empty account is an account with has **no code** and **zero nonce** and **zero balance**. This is the same as the definition in [SIP-161](/SIPS/sip-161).

On networks that undergo the merge transition, the pre state of the merge block may not contain any empty accounts. For networks that are merged at genesis, none of the genesis accounts may be empty accounts.

Rather than performing a scan of the state, clients MAY assume the following chains have no post-merge empty accounts:

1. The SilaMainnet chain whose merge block has hash `0x56a9bb0302da44b8c0b3df540781424684c3af04d0b7a38d72842b762076a664`.

2. Any chain which satisfies all of the following:

    - has no empty accounts in the genesis.

    - had a post Spurious Dragon fork at genesis.
  
The Sila specification is declared to be undefined in the presence of an empty account in a post-merge context. Any testcase involving post-merge empty accounts is invalid.

## Rationale

This SIP was drafted to be the simplest possible way of eliminating the long term technical debt imposed by empty accounts. The Merge was chosen as a natural easily identifiable cutoff point.

Alternative approaches include:

- Using an earlier cutoff point, such as block `14049881`.

- Identifying a wider range of edge case behaviour that never happened.

These approaches were rejected as being unnecessarily complicated.

## Backwards Compatibility

As SIP does not change any behaviour that can occur outside the testsuite, it has no backwards compatibility consequences.

## Security Considerations

The validity of this SIP is dependent on the assertion that all empty accounts on Sila SilaMainnet were cleared prior to the merge. This should be subject to appropriate verification.

Any networks artificially created with empty accounts will cause problems with tooling and clients.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Tue, 19 Sep 2023 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-7523</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-7523</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Revert creation in case of non-empty storage</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #7610 - Revert creation in case of non-empty storage&lt;/strong&gt; is in Last Call status. It is authored by Gary Rong (@rjl493456442), Martin Holst Swende (@holiman) and was originally created 2024-02-02. It is in the Core category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-revert-creation-in-case-of-non-empty-storage/18452&quot;&gt;https://sila-magicians.org/t/sip-revert-creation-in-case-of-non-empty-storage/18452&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP causes contract creation to throw an error when attempted at an address with pre-existing storage.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

If a contract creation is attempted due to a creation transaction, the `CREATE` opcode, the `CREATE2` opcode, or any other reason, and the destination address already has either a nonzero nonce, a nonzero code length, or non-empty storage, then the creation MUST throw as if the first byte in the init code were an invalid opcode. This change MUST apply retroactively for all existing blocks.

This SIP amends [SIP-684](/SIPS/sip-684) with one extra condition, requiring empty storage for contract deployment.

This SIP will not affect [SIP-7702](/SIPS/sip-7702), since the authority&apos;s nonce is always incremented after an authorization is applied, which conflicts with the condition required for contract deployment.

## Rationale

SIP-684 defines two conditions for contract deployment: the destination address must have zero nonce and zero code length. Unfortunately, this is not sufficient. Before [SIP-161](/SIPS/sip-161) was applied, the nonce of a newly deployed contract remained set to zero. Therefore, it was entirely possible to create a contract with a zero nonce and zero code length but with non-empty storage, if slots were set in the constructor. There exists 28 such contracts on Sila sila-mainnet at this time.

## Backwards Compatibility

This is an execution layer upgrade, and so it requires a hard fork.

## Test Cases

There exists quite a number of tests in the sila tests repo as well as in the execution spec tests, which test the scenario of deployment to targets with non-empty storage. These tests have been considered problematic in the past; Reth and EELS both intentionally implement a version of the account reset solely to pass the tests. Py-svm declared the situation impossible and never implemented account reset.

Refilling the existing tests will provide sufficient coverage for this SIP.

## Security Considerations

This SIP is a security upgrade: it enforces the immutability of deployed code.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Fri, 02 Feb 2024 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-7610</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-7610</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Network Upgrade Inclusion Stages</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #7723 - Network Upgrade Inclusion Stages&lt;/strong&gt; is in Last Call status. It is authored by Tim Beiko (@timbeiko), Alex Stokes (@ralexstokes), Ansgar Dietrichs (@adietrichs), Nixo (@nixorokish), Parithosh Jayanthi (@parithosh) and was originally created 2024-06-12. It is in the  category of type Meta. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/sip-7723-network-upgrade-inclusion-stages/20281&quot;&gt;https://sila-magicians.org/t/sip-7723-network-upgrade-inclusion-stages/20281&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

Defines the stages that SIPs go through in the process of planning network upgrades: `Proposed for Inclusion`, `Considered for Inclusion`, `Scheduled for Inclusion`, `Declined for Inclusion` and `Included`.

## Motivation

This SIP proposes definitions for the various stages SIPs go through when planning network upgrades. It also provides context and guidelines around when and how SIPs should be moved from one stage to the next.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

All SIP stages apply to a single network upgrade. SIPs must be `Proposed`, `Considered`, `Declined` or `Scheduled` separately for each network upgrade. While an SIP cannot be `Included` in two network upgrades, an SIP being `Declined for Inclusion` in a previous upgrade does not prevent it from being `Proposed`, `Considered`, `Declined` or `Scheduled` for inclusion in any future upgrade. 

The stages below are generally defined for Standards Track - Core SIPs, which must be activated synchronously by all nodes on a network. To help with prioritization and communications, non-Core SIPs may also be assigned these stages. The differences in the process and implications for non-Core SIPs are noted in each stage&apos;s definition.

### Upgrade Meta SIPs

Anyone **MAY** draft a Meta SIP to list SIPs for a network upgrade. This Meta SIP **SHOULD** include four categories in its specification section: `Proposed for Inclusion`, `Declined for Inclusion`, `Considered for Inclusion` and `Scheduled for Inclusion`. Even if a category is `TBD`, it **SHOULD** be included in the initial draft for clarity. 

When the Upgrade Meta SIP is moved to `Review`, the `Proposed for Inclusion` and the `Declined for Inclusion` list **SHOULD** be removed. When it is moved to `Last Call`, `Considered for Inclusion` lists **SHOULD** be removed, leaving only the `Scheduled for Inclusion` list.

Before the Upgrade Meta SIP is moved to `Final`, the `Scheduled for Inclusion` stage **MUST** be renamed to `Included` and contain only SIPs that were activated with the upgrade. 

### Upgrade Devnets

When preparing a network upgrade, client developers typically implement SIPs first on an ephemeral test network (upgrade devnet) to verify client interoperability before deploying to long-lived test networks. These upgrade devnets follow a naming convention of `upgradeName-devnet-version` (e.g. `pectra-devnet-0` for the first upgrade devnet of the Pectra network upgrade, `dencun-devnet-1` for the second upgrade devnet of the Dencun update, etc).

Since client developers&apos; ability to include SIPs in a network upgrade is constrained by what can be implemented and tested in these upgrade devnets, the [Considered for Inclusion](#considered-for-inclusion) and [Scheduled for Inclusion](#scheduled-for-inclusion) sections below propose aligning these statuses with SIPs&apos; implementation status in upgrade devnets.

### Proposed for Inclusion

To propose an SIP for inclusion, someone **MUST** open a pull request to add it to the `Proposed for Inclusion` (PFI) section of the Upgrade Meta SIP. The proposer of an SIP **SHOULD** serve as the primary point of contact for that SIP for the duration of the upgrade cycle or **SHOULD** designate another person to serve in that role. Reasonable pull requests **SHOULD** be merged in a timely fashion by the Upgrade Meta SIP author.

At this stage, implementation teams **SHOULD** review the SIP. For Core SIPs, this should be in the context of including it in the said upgrade. For non-Core SIPs, this should be in the context of supporting Core SIPs before the network upgrade is activated. 
 
Note that SIPs must be `Proposed for Inclusion` for each network upgrade. In other words, proposals do not &quot;carry over&quot; to the next upgrade if an SIP is not included in the one it was first proposed for. 

### Considered for Inclusion

Once client developers have reviewed an SIP which was `Proposed for Inclusion`, they **MAY** move it to the `Considered for Inclusion` (CFI) stage. Once a decision is made by client teams to move an SIP to `Considered for Inclusion`, the Upgrade Meta SIP **SHOULD** be updated to reflect this. 

`Considered for Inclusion` signals that client developers intend to attempt to include the SIP in devnets. The All Core Devs Execution (ACDE) and Consensus (ACDC) call facilitators should work with the testing teams to propose a priority ordering of CFI SIPs to be reviewed by client developers, and then reflect this prioritization in the Meta SIP. This prioritization should inform which SIPs should be included in upcoming devnets, with some flexibility if circumstances change.

Assuming it meets all the requirements for sila-mainnet deployment it **MAY** be included in the network upgrade. This stage is similar to &quot;concept ACK&quot; in other open source projects, and is not sufficient to result in deployment to sila-mainnet. 

Non-Core SIPs that are `Considered for Inclusion` **SHOULD** be supported prior to the network upgrade being activated. 

An SIP **MAY** be moved from `Considered for Inclusion` to `Declined for Inclusion` if client teams are against including the SIP in the network upgrade. 

An SIP **SHOULD** have a Python implementation accompanied by tests in [execution-specs](https://github.com/sila-chain/execution-specs/blob/78fb726158c69d8fa164e28f195fabf6ab59b915/README.md) submitted as an open PR. The SIP writer is encouraged to reach out to the maintainers of execution-specs for assistance with implementation. Client developers **MAY** decide to allow an SIP to be moved to `Considered for Inclusion` without either implementation, being aware that the absence of these implementations could lead to delays in the testing cycle.

Any updates to an SIP that is already at this stage **SHOULD** be accompanied by the appropriate updates to its implementation and tests in [execution-specs](https://github.com/sila-chain/execution-specs/blob/78fb726158c69d8fa164e28f195fabf6ab59b915/README.md) if deemed necessary by client developers.

### Declined for Inclusion

At any time during the network upgrade planning process, client developers **MAY** move SIPs from any other stage to the `Declined for Inclusion` (DFI) stage if client teams are against including the SIP in the network upgrade. Once a decision is made by client teams to move an SIP to `Declined for Inclusion`, the Upgrade Meta SIP **SHOULD** be updated to reflect this.


`Declined for Inclusion` signals that client developers wish to exclude the SIP from the current network upgrade and stop discussing its potential inclusion or implementation status in relation to this upgrade. An SIP which was `Declined for Inclusion` in a particular upgrade **MAY** still be `Proposed for Inclusion` in a subsequent upgrade. In exceptional circumstances, client developers **MAY** choose to move an SIP from `Declined for Inclusion` to `Considered for Inclusion` or `Scheduled for Inclusion`. 

### Scheduled for Inclusion

An SIP can be moved to Scheduled for Inclusion (SFI) when core developers:

1. Agree upon a strong intent to include the SIP in the next network upgrade.
2. Agree that an SIP&apos;s specifications and implementations have reached a certain level of maturity.

I.e., an SFI status signals that the SIP is on track for inclusion barring unforeseen issues.

The following critieria can be applied to ascertain whether an SIP is mature (stable) enough to move to SFI:

- It has been included in a devnet that has demonstrated stability (e.g., high participation, no critical bugs for ≥1 week).
- The SIP specification is close to final (no placeholder values or pending design decisions).
- It has minimal interactions with other CFI SIPs, OR those interactions have been tested in the same devnet.
- Testing coverage is adequate (clients pass consensus tests, no known divergence across clients).

All Core Devs Testing (ACDT) calls may review SIPs to determine SFI eligibility. Status changes will then be ratified on ACDE or ACDC before assigning SFI status.

`Scheduled for Inclusion` signals that the SIP is on track for inclusion barring unforeseen issues. SIPs with incomplete specs or untested interactions should remain CFI until these conditions are met. The latest Upgrade Devnet must contain all `Scheduled for Inclusion` Core SIPs.

An SIP **MAY** be moved from `Scheduled for Inclusion` to `Declined for Inclusion` if circumstances necessitate removal, as agreed by client teams. An SIP **MAY** also be moved from `Scheduled for Inclusion` to `Considered for Inclusion` if client teams remain in favor of including the SIP in the network upgrade but cannot commit to including it in the **next** Upgrade Devnet.

An SIP **MUST** have a Python implementation accompanied by tests in [execution-specs](https://github.com/sila-chain/execution-specs/blob/5cdb055e069e246a877c8aa018079eb0b9093f57/README.md), submitted as an open PR or merged to the `devnets/upgradeName/version` branch of the repository. Client developers **MAY** decide to allow an SIP to be moved to `Scheduled for Inclusion` without an [execution-specs](https://github.com/sila-chain/execution-specs/blob/5cdb055e069e246a877c8aa018079eb0b9093f57/README.md) implementation, but the tests are strictly mandatory.

Any updates to an SIP that is already at this stage **MUST** be accompanied by appropriate updates to its implementation and tests in [execution-specs](https://github.com/sila-chain/execution-specs/blob/5cdb055e069e246a877c8aa018079eb0b9093f57/README.md) if deemed necessary by client developers.

### Included

After network upgrade activation, all included Core SIPs and activated non-Core SIPs **MUST** be moved to `Included` in the Meta SIP. All other status lists **MUST** be removed from the Meta SIP.

`Included` signals that the SIPs have been activated as part of the network upgrade. 

## Rationale

Formalizing the `Proposed for Inclusion`, `Considered for Inclusion`, `Scheduled for Inclusion`, `Declined for Inclusion` and `Included` stages provides better legibility to both protocol maintainers and the broader Sila community.

The specification tries to minimize steps that **MUST** be followed to align with Sila&apos;s &quot;rough consensus&quot; governance model. 

Assuming it is adopted, the process outlined in this SIP should be used for at least one full network upgrade cycle before moving to `Last Call` and at least two full network upgrade cycles before moving to `Final`. This way, the SIP can be updated to reflect changes made to the process over time. 

## Backwards Compatibility

This SIP does not directly change the Sila protocol. It formalizes parts of the current network upgrade planning process. 

## Security Considerations

None.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-7723</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-7723</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Code Index</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #7744 - Code Index&lt;/strong&gt; is in Last Call status. It is authored by Tim Pechersky (@peersky) &lt;t@peersky.xyz&gt; and was originally created 2024-07-16. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/src-7744-code-index/20569&quot;&gt;https://sila-magicians.org/t/src-7744-code-index/20569&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP defines a standard interface for indexing smart contracts on Sila by their bytecode hash. This enables trustless discovery and verification of contract code, facilitating use cases like bytecode signing, whitelisting, and decentralized distribution mechanisms.

## Motivation

Existing contract discovery relies on addresses, which are non-deterministic and can be obfuscated through proxies. Indexing by bytecode hash provides a deterministic and tamper-proof way to identify and verify contract code, enhancing security and trust in the Sila ecosystem.

Consider a security auditor who wants to attest to the integrity of a contract&apos;s code. By referencing bytecode hashes, auditors can focus their audit on the bytecode itself, without needing to assess deployment parameters or storage contents. This method verifies the integrity of a contract&apos;s codebase without auditing the entire contract state.

Additionally, bytecode referencing allows whitelist contracts before deployment, allowing developers to get pre-approval for their codebase without disclosing the code itself, or even pre-setup infrastructure that will change it behavior upon adding some determined functionality on chain.

For developers relying on extensive code reuse, bytecode referencing protects against malicious changes that can occur with address-based referencing through proxies. This builds long-term trust chains extending to end-user applications.

For decentralized application (dApp) developers, a code index can save gas costs by allowing them to reference existing codebases instead of redeploying them, optimizing resource usage. This can be useful for dApps that rely on extensive re-use of same codebase as own dependencies.

### Why this registry needs to be an SRC

The Code Index is essential for trustless and secure smart contract development. By standardizing the interface for indexing contracts by their bytecode, developers can easily integrate this feature into their smart contracts, enhancing the security and trustworthiness of the Sila ecosystem.

Its simplicity and generic nature make it suitable for a wide range of applications. The ability to globally reference the same codebase makes it an ideal candidate for standardization.

Ultimately, this feature should be incorporated into SIP standards, as it is a fundamental building block for trustless and secure smart contract development. This standard is a step towards this goal.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.28;
import {ISRC7744} from &quot;./ISRC7744.sol&quot;;

/**
 * @title Byte Code Indexer Contract
 * @notice You can use this contract to index contracts by their bytecode.
 * @dev This allows to query contracts by their bytecode instead of addresses.
 * @author Tim Pechersky (@Peersky)
 */
contract SRC7744 is ISRC7744 {
    mapping(bytes32 =&gt; address) private index;

    function isSIP7702(address account) public view returns (bool) {
        bytes3 prefix;
        assembly {
            extcodecopy(account, 0, mload(0x40), 3) // Copy first 3 bytes to memory
            prefix := mload(0x40) // Load the 3 bytes from memory
        }
        return prefix == bytes3(0xef0100);
    }

    function isValidContainer(address container) private view returns (bool) {
        bytes memory code = container.code;
        bytes32 codeHash = address(container).codehash;
        return (code.length &gt; 0 &amp;&amp; codeHash != bytes32(0) &amp;&amp; !isSIP7702(container));
    }

    /**
     * @notice Registers a contract in the index by its bytecode hash
     * @param container The contract to register
     * @dev `msg.codeHash` will be used
     * @dev It will revert if the contract is already indexed or if returns SIP7702 delegated EOA
     */
    function register(address container) external {
        address etalon = index[container.codehash];
        require(isValidContainer(container), &quot;Invalid container&quot;);
        if (etalon != address(0)) {
            if (isValidContainer(etalon)) revert alreadyExists(container.codehash, container);
        }
        index[container.codehash] = container;
        emit Indexed(container, container.codehash);
    }

    /**
     * @notice Returns the contract address by its bytecode hash
     * @dev returns zero if the contract is not indexed
     * @param id The bytecode hash
     * @return The contract address
     */
    function get(bytes32 id) external view returns (address) {
        return index[id];
    }
}
```

### Deployment method

The `CodeIndex` contract is deployed at: `0xC0De1D1126b6D698a0073A4e66520111cEe22F62` using `CREATE2` via the deterministic deployer at `0x4e59b44847b379578588920ca78fbf26c0b4956c` with a salt of `0x9425035d50edcd7504fe5eeb5df841cc74fe6cccd82dca6ee75bcdf774bd88d9` is obtained by seeking a vanity address starting with meaningful name &quot;Code ID (`c0de1d`) for a bytecode compiled with `solc 0.8.28` as `solc --input-file src/SRC7744.sol --bin --optimize --optimize-runs 2000 --metadata-hash none --via-ir --optimize-yul`

## Rationale

**Bytecode over Addresses**: Bytecode is deterministic and can be verified on-chain, while addresses are opaque and mutable.

**Reverting on re-indexing**: There is small, yet non-zero probability of hash collision attack. Disallowing updates to indexed location of bytecode coupes with this.

**Simple Interface**: The interface is minimal and focused to maximize composability and ease of implementation.

**Library Implementation**: Implementing this as a library would limit its impact, making code reuse more difficult and lacking a single, official source of truth. By establishing this as an SRC, we ensure standardization and widespread adoption, driving the ecosystem forward.

## Reference Implementation

Reference implementation of the Code Index can be found in the assets folder. There you can find the [interface](/assets/sip-7744/ISRC7744.sol) and the [implementation](/assets/sip-7744/SRC7744.sol) of the Code Index.

## Security Considerations

**Malicious Code**: The index does NOT guarantee the safety or functionality of indexed contracts. Users MUST exercise caution and perform their own due diligence before interacting with indexed contracts.

**Storage contents of registered contracts**: The index only refers to the bytecode of the contract, not the storage contents. This means that the contract state is not indexed and may change over time.

**[SIP-7702]**: The index does not index the SIP-7702 delegated accounts. During attempt to register, it checks if contract code begins with reserved delegation designator `0xef0100` and if so, it will revert.

**Self-Destruct Contracts**: In case of indexed contract storage becomes empty, contracts may be re-indexed, During register function call, if contract is already indexed, we run `isValidContainer` check on the indexed address. It it fails, re-indexing is allowed with a newly specified address.

[SIP-7702]: /SIPS/sip-7702

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Tue, 16 Jul 2024 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-7744</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-7744</guid>
      </item>
      
    
      
    
      
      
      <item>
        <title>Composable Security Middleware Hooks</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #7746 - Composable Security Middleware Hooks&lt;/strong&gt; is in Last Call status. It is authored by Tim Pechersky (@peersky) and was originally created 2024-07-17. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/src-7746-composable-security-middleware-hooks/19471&quot;&gt;https://sila-magicians.org/t/src-7746-composable-security-middleware-hooks/19471&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This SIP proposes a standard interface, `ILayer`, for implementing composable security layers in smart contracts. These layers act as middleware, enabling runtime validation of function calls before and after execution, independent of the protected contract&apos;s logic. This approach facilitates modular security, allowing independent providers to manage and upgrade security layers across multiple contracts.

## Motivation

Current smart contract security practices often rely on monolithic validation logic within the contract itself. This can lead to tightly coupled code, making it difficult to isolate and address security concerns. Better structured architecture is needed, middleware like approach is widely used in the industry, allowing to wrap calls in other calls in generic and repeatable pattern with same call signatures.

The Security Layers Standard introduces a modular approach, enabling:

- **Independent Security Providers:** Specialized security providers can focus on developing and maintaining specific security checks.
- **Composable Security:** Layers can be combined to create comprehensive security profiles tailored to individual contract needs.
- **Upgradability:** Security layers can be updated without requiring changes to the protected contract.
- **Flexibility:** Layers can perform a wide range of validation checks, including access control, input sanitization, output verification, and more.

Having a generalized standard for such layers can help to build more secure and modular systems as well as enable security providers to build generic, service-oriented security oracle solutions.

## Specification

A contract implementing the `ILayer` interface MUST provide two functions:

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.20;

interface ILayer {
    /// @notice Validates a function call before execution.
    /// @param configuration Layer-specific configuration data.
    /// @param selector The function selector being called.
    /// @param sender The address initiating the call.
    /// @param value The amount of SIL sent with the call (if any).
    /// @param data The calldata for the function call.
    /// @return beforeCallResult Arbitrary data to be passed to `afterCallValidation`.
    /// @dev MUST revert if validation fails.
    function beforeCall(
        bytes memory configuration,
        bytes4 selector,
        address sender,
        uint256 value,
        bytes memory data
    ) external returns (bytes memory);

    /// @notice Validates a function call after execution.
    /// @param configuration Layer-specific configuration data.
    /// @param selector The function selector being called.
    /// @param sender The address initiating the call.
    /// @param value The amount of SIL sent with the call (if any).
    /// @param data The calldata for the function call.
    /// @param beforeCallResult The data returned by `beforeCallValidation`.
    /// @dev MUST revert if validation fails.
    function afterCall(
        bytes memory configuration,
        bytes4 selector,
        address sender,
        uint256 value,
        bytes memory data,
        bytes memory beforeCallResult
    ) external;
}

```

A protected contract MAY integrate security layers by calling the `beforeCallValidation` function before executing its logic and the `afterCallValidation` function afterwards. Multiple layers can be registered and executed in a defined order. The protected contract MUST revert if any layer reverts.

## Rationale

**Flexibility**: The `layerConfig` parameter allows for layer-specific customization, enabling a single layer implementation to serve multiple contracts with varying requirements.

**non-static calls**: Layers can maintain their own state, allowing for more complex validation logic (e.g., rate limiting, usage tracking).

**Strict Validation**: Reverts on validation failure ensure a fail-safe mechanism, preventing execution of potentially harmful transactions.

**Gas Costs**: Layers naturally will have gas costs associated with their execution. However, the benefits of enhanced security and modularity outweigh these costs, especially as blockchain technology continues to evolve and we expect gas costs to decrease over time.

## Reference Implementation

A reference implementation of the `ILayer` interface and a sample protected contract can be found in the repository:
In the [`ILayer.sol`](/assets/sip-7746/ILayer.sol) a reference interface is provided.

In this test, a [`Protected.sol`](/assets/sip-7746/test/Protected.sol) contract is protected by a [`RateLimitLayer.sol`](/assets/sip-7746/test/RateLimitLayer.sol) layer. The `RateLimitLayer` implements the `ILayer` interface and enforces a rate which client has configured.
The `Drainer` simulates a vulnerable contract that acts in a malicious way. In the `test.ts` The `Drainer` contract is trying to drain the funds from the `Protected` contract. It is assumed that `Protected` contract has bug that allows partial unauthorized access to the state.
The `RateLimitLayer` is configured to allow only 10 transactions per block from same sender. The test checks that the `Drainer` contract is not able to drain the funds from the `Protected` contract.

## Security Considerations

**Layer Trust**: Thoroughly audit and vet any security layer before integrating it into your contract. Malicious layers can compromise contract security.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Wed, 17 Jul 2024 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-7746</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-7746</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Store, Table-Based Introspectable Storage</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #7813 - Store, Table-Based Introspectable Storage&lt;/strong&gt; is in Last Call status. It is authored by alvarius (@alvrs), dk1a (@dk1a), frolic (@frolic), ludens (@ludns), vdrg (@vdrg), yonada &lt;yonada@proton.me&gt; and was originally created 2024-11-08. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/src-7813-store-table-based-introspectable-storage/21628&quot;&gt;https://sila-magicians.org/t/src-7813-store-table-based-introspectable-storage/21628&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This standard introduces a flexible on-chain storage pattern that organizes data into structured tables that consist of records with fixed key and value schemas, similar to a traditional database. This storage pattern consists of a unified contract interface for data access, along with a compact binary encoding format for both static and dynamic data types. State changes are tracked through standardized events that enable automatic, schema-aware state replication by off-chain indexers. New tables can be dynamically registered at runtime through a special table that stores schema metadata for all tables, allowing the system to evolve without breaking existing contracts or integrations.

## Motivation

The absence of consistent standards for on-chain data management in smart contracts can lead to rigid implementations, tightly coupled contract logic with off-chain services, and challenges in updating or extending a contract’s data layout without breaking existing integrations.

Using the storage mechanism defined in this SRC provides the following benefits:

1. **Automatic Indexing**: By emitting consistent, standardized events during state changes, off-chain services can automatically track on-chain state and provide schema-aware indexer APIs.
2. **Elimination of Custom Getter Functions**: Any contract or off-chain service can read stored data through a consistent interface, decoupling smart contract implementation from specific data access patterns and reducing development overhead.
3. **Simpler Upgradability**: This pattern leverages unstructured storage, making it easier to upgrade contract logic without the risks associated with using a fixed storage layout.
4. **Flexible Data Extensions**: New tables can be added at runtime without without breaking existing integrations with other data consumers.
5. **Reduced gas costs**: Using efficient data packing reduces gas costs for both storage and event emissions.

## Specification

### Definitions

#### Store

A smart contract that implements the interface proposed by this SRC and organizes data in Tables. It emits events for each data operation so that off-chain components can replicate the state of all tables.

#### Table

A storage structure that holds **Records** sharing the same **Schema**.

- **On-chain Table**: Stores its state on-chain and emits events for off-chain- indexers.
- **Off-chain Table**: Does not store state on-chain but emits events for off-chain indexers.

#### Record

A piece of data stored in a **Table**, addressed by one or more keys.

#### `ResourceId`

A 32-byte value that uniquely identifies each **Table** within the **Store**.

```solidity
type ResourceId is bytes32;
```

Encoding:

| **Bytes (from left to right)** | **Description**       |
| ------------------------------ | --------------------- |
| 0-1                            | Table type identifier |
| 2-31                           | Unique identifier     |

**Table Type Identifiers:**

- `0x7462` (`&quot;tb&quot;`) for on-chain tables
- `0x6f74` (`&quot;ot&quot;`) for off-chain tables

#### `Schema`

Used to represent the layout of Records within a table.

```solidity
type Schema is bytes32;
```

Each Table defines two schemas:

- Key Schema: the types of the keys used to uniquely identify a **Record** within a table. It consists only of fixed-length data types.
- Value Schema: the types of the value fields of a **Record** within a table, which can include both fixed-length and variable-length data types.

| **Byte(s) from left to right** | **Value**                          | **Constraint**                                                    |
| ------------------------------ | ---------------------------------- | ----------------------------------------------------------------- |
| 0-1                            | Total byte length of static fields |                                                                   |
| 2                              | Number of static length fields     | ≤ (28 - number of dynamic length fields)                          |
| 3                              | Number of dynamic length fields    | For the key schema, 0                                             |
| For the value schema, ≤5       |
| 4-31                           | Each byte encodes a `SchemaType`   | Dynamic-length types MUST come after all the static-length types. |

#### `SchemaType`

Single byte that represents the type of a specific static or dynamic field.

```solidity
enum SchemaType { ... }
```

##### Type Encoding

| Value Range      | Type                                        |
| ---------------- | ------------------------------------------- |
| `0x00` to `0x1F` | `uint8` to `uint256` (increments of 8 bits) |
| `0x20` to `0x3F` | `int8` to `int256` (increments of 8 bits)   |
| `0x40` to `0x5F` | `bytes1` to `bytes32`                       |
| `0x60`           | `bool`                                      |
| `0x61`           | `address`                                   |
| `0x62` to `0x81` | `uint8[]` to `uint256[]`                    |
| `0x82` to `0xA1` | `int8[]` to `int256[]`                      |
| `0xA2` to `0xC1` | `bytes1[]` to `bytes32[]`                   |
| `0xC2`           | `bool[]`                                    |
| `0xC3`           | `address[]`                                 |
| `0xC4`           | `bytes`                                     |
| `0xC5`           | `string`                                    |

#### `FieldLayout`

Encodes the concrete value `Schema` information, specifically the total byte length of the static fields, the number of dynamic fields and the length of each static field on its own.

This encoding serves as an optimization for on-chain operations. By having the exact lengths readily available, the Store doesn&apos;t need to repeatedly compute or translate the schema definitions into actual field lengths during execution.

```solidity
type FieldLayout is bytes32;
```

| **Byte(s) from left to right** | **Value**                                                           | **Constraint**                           |
| ------------------------------ | ------------------------------------------------------------------- | ---------------------------------------- |
| 0-1                            | Total length of static fields                                       |                                          |
| 2                              | Number of static length fields                                      | ≤ (28 - number of dynamic length fields) |
| 3                              | Number of dynamic length fields                                     | For the key schema, 0                    |
| For the value schema, ≤5       |
| 4-31                           | Each byte encodes the byte length of the corresponding static field |                                          |

#### `EncodedLengths`

Encodes the byte length of all the dynamic fields of a specific Record. It is returned by the Store methods when reading a Record, as it is needed for decoding dynamic fields.

```solidity
type EncodedLengths is bytes32;
```

| Bytes (from least to most significant) | Type   | Description                        |
| -------------------------------------- | ------ | ---------------------------------- |
| 0x00-0x06                              | uint56 | Total byte length of dynamic data  |
| 0x07-0xB                               | uint40 | Length of the first dynamic field  |
| 0x0C-0x10                              | uint40 | Length of the second dynamic field |
| 0x11-0x15                              | uint40 | Length of the third dynamic field  |
| 0x16-0x1A                              | uint40 | Length of the fourth dynamic field |
| 0x1B-0x1F                              | uint40 | Length of the fifth dynamic field  |

### Packed Data Encoding

Record data returned by Store methods and included in Store events uses the following encoding rules.

#### Field Limits

- **Maximum Total Fields**: A record can contain up to **28 fields** in total (both static and dynamic fields combined).
  - This limit is due to the `Schema` type structure, which uses 28 bytes (bytes 4 to 31) to define field types, with one byte per field (`SchemaType`).
- **Dynamic Fields Limit**: A record can have up to **5 dynamic fields**.
  - This is due to the fact that a single 32 bytes word (`EncodedLengths`) to encode the byte lengths of each dynamic field, instead of encoding each length separately as Solidity’s `abi.encode` would.
- **Static Fields Limit**: The maximum number of static fields is **28 minus the number of dynamic fields**.
  - For example, if there are 5 dynamic fields, the maximum number of static fields is 23 (28 - 5).

#### Encoding Rules

- Static-length fields are encoded without any padding, and concatenated in the order they are defined in the schema, which is equivalent to using Solidity&apos;s `abi.encodePacked`.
- For dynamic-length fields (arrays, `bytes`, and `string`s):
  - If the field is an array, its elements are tightly packed without padding.
  - All dynamic fields are concatenated together without padding and without including their lengths.
  - The lengths of all dynamic fields are encoded into a single `EncodedLengths`.

#### Example

Suppose a table has the following value schema:

```solidity
(uint256 id, address owner, string description, uint8[] scores)
```

**Encoding (Pseudocode)**:

```solidity
bytes memory staticData = abi.encodePacked(id, owner);

// This is a custom function as Solidity does not provide a way to tightly pack array elements
bytes memory packedScores = packElementsWithoutPadding(scores);

// abi.encodePacked concatenates both description and packedScores without including their lengths
bytes memory dynamicData = abi.encodePacked(description, packedScores);

// Total length is encoded in the 56 least significant bits
EncodedLengths encodedLengths = dynamicData.length;

// Each length is encoded using 5 bytes
encodedLengths |= (description.length &lt;&lt; (56));
encodedLengths |= (encodedData.length &lt;&lt; (56 + 8 * 5));

// The full encoded record data is represented by the following tuple:
// (staticData, encodedLengths, dynamicData)
```

### Store Interface

All Stores MUST implement the following interface.

```solidity
interface IStore {
  /**
   * Get full encoded record (all fields, static and dynamic data) for the given tableId and key tuple.
   */
  function getRecord(
    ResourceId tableId,
    bytes32[] calldata keyTuple
  ) external view returns (bytes memory staticData, EncodedLengths encodedLengths, bytes memory dynamicData);

  /**
   * Get a single encoded field from the given tableId and key tuple.
   */
  function getField(
    ResourceId tableId,
    bytes32[] calldata keyTuple,
    uint8 fieldIndex
  ) external view returns (bytes memory data);

  /**
   * Get the byte length of a single field from the given tableId and key tuple
   */
  function getFieldLength(
    ResourceId tableId,
    bytes32[] memory keyTuple,
    uint8 fieldIndex
  ) external view returns (uint256);
}
```

The return values of both `getRecord` and `getField` use the encoding rules previously defined in the Packed Data Encoding section. More specifically, `getRecord` returns the fully encoded record tuple, and the data returned by `getField` is encoded using the encoding rules as if the field was being encoded on its own.

### Store Operations and Events

This standard defines three core operations for manipulating records in a table: setting, updating, and deleting. For each operation, specific events must be emitted. The implementation details of these operations are left to the discretion of each Store implementation.

The fundamental requirement is that for on-chain tables the Record data retrieved through the Store interface methods at any given block MUST be consistent with the Record data that would be obtained by applying the operations implied by the Store events up to that block. This ensures data integrity and allows for accurate off-chain state reconstruction.

#### `Store_SetRecord`

Setting a Record means overwriting all of its fields. This operation can be performed whether the record has been set before or not (the standard does not enforce existence checks).

The `Store_SetRecord` event **MUST** be emitted whenever the full data of a record has been overwritten.

```solidity
event Store_SetRecord(
  ResourceId indexed tableId,
  bytes32[] keyTuple,
  bytes staticData,
  EncodedLengths encodedLengths,
  bytes dynamicData
);
```

Parameters:

| **Name**       | **Type**       | **Description**                                                                              |
| -------------- | -------------- | -------------------------------------------------------------------------------------------- |
| tableId        | ResourceId     | The ID of the table where the record is set                                                  |
| keyTuple       | bytes32[]      | An array representing the composite key for the record                                       |
| staticData     | bytes          | The static data of the record using packed encoding                                          |
| encodedLengths | EncodedLengths | The encoded lengths of the dynamic data of the record                                        |
| dynamicData    | bytes          | The dynamic data of the record, using [custom packed encoding](#packed-data-encoding)        |

#### `Store_SpliceStaticData`

Splicing the static data of a Record consists in overwriting bytes of the packed encoded static fields. The total length of static data does not change as it is determined by the table’s value schema.

The `Store_SpliceStaticData` event MUST be emitted whenever the static data of the Record has been spliced.

```solidity
event Store_SpliceStaticData(
  ResourceId indexed tableId,
  bytes32[] keyTuple,
  uint48 start,
  bytes data
);
```

Parameters:

| **Name** | **Type**   | **Description**                                               |
| -------- | ---------- | ------------------------------------------------------------- |
| tableId  | ResourceId | The ID of the table where the data is spliced                 |
| keyTuple | bytes32[]  | An array representing the key for the record                  |
| start    | uint48     | The start position in bytes for the splice operation          |
| data     | bytes      | Packed ABI encoding of a tuple with the value&apos;s static fields |

#### `Store_SpliceDynamicData`

Splicing the dynamic data of a Record involves modifying the packed encoded representation of its dynamic fields by removing, replacing, and/or inserting new bytes in place.

The `Store_SpliceDynamicData` event MUST be emitted whenever the dynamic data of the Record has been spliced.

```solidity
event Store_SpliceDynamicData(
  ResourceId indexed tableId,
  bytes32[] keyTuple,
  uint8 dynamicFieldIndex,
  uint48 start,
  uint40 deleteCount,
  EncodedLengths encodedLengths,
  bytes data
);
```

Parameters:

| **Name**          | **Type**       | **Description**                                                                                                                                          |
| ----------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| tableId           | ResourceId     | The ID of the table where the data is spliced                                                                                                            |
| keyTuple          | bytes32[]      | An array representing the composite key for the record                                                                                                   |
| dynamicFieldIndex | uint8          | The index of the dynamic field to splice data, relative to the start of the dynamic fields (Dynamic field index = field index - number of static fields) |
| start             | uint48         | The start position in bytes for the splice operation                                                                                                     |
| deleteCount       | uint40         | The number of bytes to delete in the splice operation                                                                                                    |
| encodedLengths    | EncodedLengths | The resulting encoded lengths of the dynamic data of the record                                                                                          |
| data              | bytes          | The data to insert into the dynamic data of the record at the start byte                                                                                 |

#### `Store_DeleteRecord`

The `Store_DeleteRecord` event MUST be emitted whenever the Record has been deleted from the Table.

```solidity
event Store_DeleteRecord(ResourceId indexed tableId, bytes32[] keyTuple);
```

Parameters:

| **Name** | **Type**   | **Description**                                        |
| -------- | ---------- | ------------------------------------------------------ |
| tableId  | ResourceId | The ID of the table where the record is deleted        |
| keyTuple | bytes32[]  | An array representing the composite key for the record |

See the [reference implementation section](#reference-implementation) for an example on how to index store events.

### The `Tables` table

To keep track of the information of each table and support registering new tables at runtime, the Store implementation MUST include a special on-chain `Tables` table, which behaves the same way as other on-chain tables except for the special constraints mentioned below.

The `Tables` table MUST use the following `Schema`s:

- Key Schema:
  - `tableId` (`ResourceId`): `ResourceId` of the table this record describes.
- Value Schema:
  - `fieldLayout` (`FieldLayout`): encodes the byte length of each static data type in the table.
  - `keySchema` (`Schema`): represents the data types of the (composite) key of the table.
  - `valueSchema` (`Schema`): represents the data types of the value fields of the table.
  - `abiEncodedKeyNames` (`bytes`): ABI encoded string array of key names.
  - `abiEncodedFieldNames` (`bytes`): ABI encoded string array of field names.

Records stored in the `Tables` table are considered immutable:

- The `Store` MUST emit a single `Store_SetRecord` event for each table being registered.
- The `Store` SHOULD NOT emit any other `Store` events for a `Table` registered in the `Tables` table.

The `Tables` table MUST store a record that describes itself before any other table is registered, emitting the corresponding `Store_SetRecord` event. The record must use the following `tableId`:

```solidity
// First two bytes indicates that this is an on-chain table
// The next 30 bytes are the unique identifier for the Tables table
// bytes32(&quot;tb&quot;) | bytes32(&quot;store&quot;) &gt;&gt; (2 * 8) | bytes32(&quot;Tables&quot;) &gt;&gt; (2 * 8 + 14 * 8)
ResourceId tableId = ResourceId.wrap(0x746273746f72650000000000000000005461626c657300000000000000000000);
```

By using a predefined `ResourceId` and `Schema` for the `Tables` table, off-chain indexers can interpret store events for all registered tables. This enables the development of advanced off-chain services that operate on structured data rather than raw encoded data like in the previous indexer implementation example.

## Rationale

### Splice Events

While the `Store_SetRecord` event suffices for tracking the data of each record off-chain, including `Splice` events (`Store_SpliceStaticData` and `Store_SpliceDynamicData`) allows for more efficient partial updates. When only a portion of a record changes, emitting a full `SetRecord` event would be inefficient because the entire record data would need to be read from storage and emitted. `Splice` events enable the store to emit only the minimal necessary data for the update, reducing gas consumption. This is particularly important for records with large dynamic fields, as the cost of updating them doesn’t grow with the field’s size.

### Disallowing Arrays of Dynamic Types

Arrays of dynamic types (e.g., `string[]`, `bytes[]`) are intentionally not included as supported `SchemaType`s. This restriction enforces a flat data schema, which simplifies the store implementation and enhances efficiency. If users need to store such data structures, they can model them using a separate table with a schema like `{ index: uint256, data: bytes }`, where each array element is represented as an individual record.

### FieldLayout Optimization

Including the `FieldLayout` in the `Tables` schema provides an on-chain optimization by precomputing and storing the exact byte lengths of static fields. This eliminates the need to repeatedly compute field lengths and offsets during runtime, which can be gas-intensive. By having this information readily available, the store can perform storage operations more efficiently, while components reading from the store can retrieve it from the `Tables` table to decode the corresponding records.

### Special `Tables` table

Including a special `Tables` table provides significant benefits for off-chain indexers. While emitting events for table registration isn&apos;t strictly necessary for basic indexers that operate on raw encoded data, doing so makes indexers aware of the schemas used by each table. This awareness enables the development of more advanced, schema-aware indexer APIs (e.g., SQL-like query capabilities), enhancing the utility and flexibility of off-chain data interactions.

By reusing existing Store abstractions for table registration, we also simplify the implementation and eliminate the need for additional, specific table registration events. Indexers can leverage the standard Store events to access schema information, ensuring consistency and reducing complexity.

## Reference Implementation

### Store Event Indexing

The following example shows how a simple in-memory indexer can use the Store events to replicate the Store state off-chain. It is important to note that this indexer operates over raw encoded data which is not that useful on its own, but can be improved as we will explain in the next section.

We use TypeScript for this example but it can easily be replicated with other languages.

```tsx
type Hex = `0x${string}`;

type Record = {
  staticData: Hex;
  encodedLengths: Hex;
  dynamicData: Hex;
};

const store = new Map&lt;string, Record&gt;();

// Create a key string from a table ID and key tuple to use in our store Map above
function storeKey(tableId: Hex, keyTuple: Hex[]): string {
  return `${tableId}:${keyTuple.join(&quot;,&quot;)}`;
}

// Like `Array.splice`, but for strings of bytes
function bytesSplice(
  data: Hex,
  start: number,
  deleteCount = 0,
  newData: Hex = &quot;0x&quot;
): Hex {
  const dataNibbles = data.replace(/^0x/, &quot;&quot;).split(&quot;&quot;);
  const newDataNibbles = newData.replace(/^0x/, &quot;&quot;).split(&quot;&quot;);
  return `0x${dataNibbles
    .splice(start, deleteCount * 2)
    .concat(newDataNibbles)
    .join(&quot;&quot;)}`;
}

function bytesLength(data: Hex): number {
  return data.replace(/^0x/, &quot;&quot;).length / 2;
}

function processStoreEvent(log: StoreEvent) {
  if (log.eventName === &quot;Store_SetRecord&quot;) {
    const key = storeKey(log.args.tableId, log.args.keyTuple);

    // Overwrite all of the Record&apos;s fields
    store.set(key, {
      staticData: log.args.staticData,
      encodedLengths: log.args.encodedLengths,
      dynamicData: log.args.dynamicData,
    });
  } else if (log.eventName === &quot;Store_SpliceStaticData&quot;) {
    const key = storeKey(log.args.tableId, log.args.keyTuple);
    const record = store.get(key) ?? {
      staticData: &quot;0x&quot;,
      encodedLengths: &quot;0x&quot;,
      dynamicData: &quot;0x&quot;,
    };

    // Splice the static field data of the Record
    store.set(key, {
      staticData: bytesSplice(
        record.staticData,
        log.args.start,
        bytesLength(log.args.data),
        log.args.data
      ),
      encodedLengths: record.encodedLengths,
      dynamicData: record.dynamicData,
    });
  } else if (log.eventName === &quot;Store_SpliceDynamicData&quot;) {
    const key = storeKey(log.args.tableId, log.args.keyTuple);
    const record = store.get(key) ?? {
      staticData: &quot;0x&quot;,
      encodedLengths: &quot;0x&quot;,
      dynamicData: &quot;0x&quot;,
    };

    // Splice the dynamic field data of the Record
    store.set(key, {
      staticData: record.staticData,
      encodedLengths: log.args.encodedLengths,
      dynamicData: bytesSplice(
        record.dynamicData,
        log.args.start,
        log.args.deleteCount,
        log.args.data
      ),
    });
  } else if (log.eventName === &quot;Store_DeleteRecord&quot;) {
    const key = storeKey(log.args.tableId, log.args.keyTuple);

    // Delete the whole Record
    store.delete(key);
  }
}
```

## Security Considerations

### Access Control

This standard only defines functions to **read** from the Store (`getRecord`, `getField`, and `getFieldLength`). The methods for setting or modifying records in the store are left to each specific implementation. Therefore, implementations **must provide appropriate access control mechanisms** for writing to the store, tailored to their specific use cases.

### On-Chain Data Accessibility

All data stored within a store is accessible not only off-chain but also **on-chain** by other smart contracts through the provided read functions (`getRecord`, `getField`, and `getFieldLength`). This differs from the typical behavior of smart contracts, where internal storage variables are private by default and cannot be directly read by other contracts unless explicit getter functions are provided. Thus, developers must be mindful that any data stored in the store is openly accessible to other smart contracts.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Fri, 08 Nov 2024 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-7813</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-7813</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Confidential Transactions Supported Token</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #7945 - Confidential Transactions Supported Token&lt;/strong&gt; is in Last Call status. It is authored by Siyuan Zheng (@andrewcoder666) &lt;zhengsiyuan.zsy@antgroup.com&gt;, Zhe Han (@iampkuhz) &lt;hanzhe.hz@ant-intl.com&gt;, Xiaoyu Liu (@elizabethxiaoyu) &lt;jiushi.lxy@antgroup.com&gt;, Wenwei Ma (@madyinglight) &lt;huiwei.mww@antgroup.com&gt;, Jun Meng Tan (@chadxeth) &lt;junmeng.t@antgroup.com&gt;, Yuxiang Fu (@tmac4096) &lt;kunfu.fyx@antgroup.com&gt;, Kecheng Gao (@thanks-v-me-50) &lt;gaokecheng.gkc@antgroup.com&gt;, Alwin Ng Jun Wei (@alwinngjw) &lt;alwin.ng@antgroup.com&gt;, Chenxin Wang (@3235773541) &lt;wcx465603@antgroup.com&gt;, Xiang Gao (@GaoYiRu) &lt;gaoxiang.gao@antgroup.com&gt;, yuanshanhshan (@xunayuan) &lt;yuanshanshan.yss@antgroup.com&gt;, Hao Zou (@BruceZH0915) &lt;situ.zh@antgroup.com&gt;, Yanyi Liang &lt;eason.lyy@antgroup.com&gt;, Yuehua Zhang (@astroyhzcc) &lt;ruoying.zyh@antgroup.com&gt; and was originally created 2025-05-09. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/interface-of-confidential-transactions-supported-token-contract/23586&quot;&gt;https://sila-magicians.org/t/interface-of-confidential-transactions-supported-token-contract/23586&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract
Classic token contracts like [SRC-20](/SIPS/sip-20) enable their token holders to make transfers and/or approve others to make transfers on their behalves. The generality of token standard [SRC-20](/SIPS/sip-20) catalyzed decentralized finance and many other blockchain applications. However, when it comes to privacy, although some technical schemes have been proposed, few standards have been established, which limits the evolution of privacy-preserving blockchain applications.

This proposal draws up a standard interface for fungible token contracts supporting confidential transactions. It provides basic transfer functionality without loss of generality, and allowance and approve functionalities. Contracts following the standard can provide confidentiality for users&apos; balances and token transfer value, and can enable other blockchain applications to make transfers on behalf of owners, which empowers more privacy-preserving capabilities for blockchain applications.

## Motivation
Confidential transactions have been implemented in many blockchains, either natively through blockchain protocols like Monero and Zcash, or through smart contracts like Zether[^1] without modifying the blockchain protocol.

However, few standards are proposed on Sila (and/or other SVM-compatible blockchains) to illustrate privacy-preserving contracts without modifying the underlying protocol. Users and applications cannot easily detect whether a token contract supports confidential transactions or not, and so cannot reliably make transfers without revealing the actual amount.

Consequently, this proposal is to standardize confidential-transaction-supported token contracts, without loss of generality, by only specifying core methods and events.

Such a standard interface allows confidential transactions of tokens to be applied by certain parties that are sensitive to transfer amounts, or by privacy-preserving applications.

Compared with application-specific confidential token designs, such as Confidential Fungible Token using `bytes32` pointers representing confidential balances, open-source projects Tornado Cash and Zeto implementing a UTXO model in smart contracts, this proposal standardizes only the minimum interoperable surface in the setting of an account-based model: balance queries, transfers, delegated transfers, approvals, and related events. This allows different proof systems, ciphertext encodings, and compliance workflows to coexist behind a common interface, so wallets, bridges, exchanges, and other applications can support confidential tokens without being tightly coupled to one implementation. 

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.


### Contract Interface
Compliant contracts MUST implement the following interface:

```solidity
interface ISRC7945 {
    function confidentialBalanceOf(address owner) external view returns (bytes memory confidentialBalance);

    function confidentialTransfer(
        address _to,
        bytes memory _confidentialTransferValue,
        bytes memory _proof
    ) external;

    function confidentialTransferFrom(
        address _from,
        address _to,
        bytes memory _confidentialTransferValue,
        bytes memory _proof
    ) external;

    function confidentialApprove(
        address _spender,
        bytes memory _confidentialValue,
        bytes memory _proof
    ) external;

    function confidentialAllowance(
        address _owner,
        address _spender
    ) external view returns (bytes memory _confidentialValue);

    event ConfidentialTransfer(
        address indexed _spender,
        address indexed _from,
        address indexed _to,
        bytes _confidentialTransferValue
    );

    event ConfidentialApproval(
        address indexed _owner,
        address indexed _spender,
        bytes _currentAllowancePart,
        bytes _allowancePart
    );
}
```

Additionally, compliant contracts MAY implement the following interface:

```solidity
interface ISRC7945Metadata {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
}
```

#### `ISRC7945Metadata`
##### Methods
###### `name`
```solidity
function name() external view returns (string memory)
```

Returns the name of the token - e.g. `&quot;MyConfidentialToken&quot;`.

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect this value to be present.

###### `symbol`
```solidity
function symbol() external view returns (string memory)
```

Returns the symbol of the token, e.g. `&quot;cHIX&quot;`.

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect this value to be present.

###### `decimals`
```solidity
function decimals() external view returns (uint8)
```

Returns the number of decimals the token uses - e.g. `8`, meaning the token amount should be divided by `100000000` to get its user representation.

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect this value to be present.

#### `ISRC7945`
##### Methods

###### `confidentialBalanceOf`

```solidity
function confidentialBalanceOf(address owner) 
external view returns (bytes memory confidentialBalance)
```

Returns the confidential balance of the account with address `owner`.

###### `confidentialTransfer`

```solidity
function confidentialTransfer(
  address _to,
  bytes memory _confidentialTransferValue, 
  bytes memory _proof
) external
```

Transfers `value` amount of tokens (behind `_confidentialTransferValue`) to address `_to`, and MUST fire the `ConfidentialTransfer` event. The function SHOULD `revert` if the message caller&apos;s `_proof` of this transfer fails to be verified.

Note:

+ Implementations can fully customize the proof system, (de)serialization strategies of `bytes`, and/or the business workflow. For example, when implementing &quot;Zether&quot;[^1] confidential token contracts, the `_confidentialTransferValue` and accounts&apos; confidential balances will be encrypted homomorphically under ElGamal public keys, and `_proof` will consist of 3 parts to check:
    - `_confidentialTransferValue` is well encrypted under both the caller&apos;s public key and `_to`&apos;s;
    - The plaintext `value` behind `_confidentialTransferValue` is non-negative;
    - The caller&apos;s confidential balance is actually enough to pay the plaintext `value` behind `_confidentialTransferValue`.

###### `confidentialTransferFrom`

```solidity
function confidentialTransferFrom(
  address _from,
  address _to,
  bytes memory _confidentialTransferValue,
  bytes memory _proof
) external

```

Transfers `value` amount of tokens (behind `_confidentialTransferValue`) from address `_from` to address `_to`, and MUST fire the `ConfidentialTransfer` event.

The `confidentialTransferFrom` method is used for a withdrawal workflow, allowing contracts to transfer tokens on your behalf. This can be used, for example, to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULD `revert` unless the `_from` account has deliberately authorized the sender of the message via some mechanism, and SHOULD `revert` if the message caller&apos;s `_proof` of this transfer fails to be verified.

Note:

+ Implementations can fully customize the proof system, (de)serialization strategies of `bytes`, and/or the business workflow. For example, when implementing &quot;Zether&quot; confidential token contracts, the `_confidentialTransferValue` and accounts&apos; confidential balances will be encrypted homomorphically under ElGamal public keys, and `_proof` will consist of 3 parts to check:
    - `_confidentialTransferValue` is well encrypted under public keys of `_from`&apos;s, `_to`&apos;s, and caller&apos;s;
    - The plaintext `value` behind `_confidentialTransferValue` is non-negative;
    - The caller&apos;s confidential allowance is actually enough to pay the plaintext `value` behind `_confidentialTransferValue`.

###### `confidentialApprove`

```solidity
function confidentialApprove(
  address _spender,
  bytes memory _confidentialValue, 
  bytes memory _proof
) external
```

Allows `_spender` to withdraw from caller&apos;s split part of balances multiple times, up to the amount (allowance value) behind `_confidentialValue` to 0. This function SHOULD `revert` if the message caller&apos;s `_proof` of this transfer fails to be verified.

Caution:

This function behaves much **differently from** `approve(address,uint256)` in [SRC-20](/SIPS/sip-20).

Calling `confidentialApprove` splits the confidential balance of caller&apos;s account into *allowance part* and *the left part*.

The values behind two parts above after calling `confidentialApprove`, and the value behind the original confidential balance of caller&apos;s account before calling `confidentialApprove`, satisfy the equation:

$$ 
value_{Behind\ Allowance\ Part} + value_{Behind\ Left\ Part} = value_{Behind\ Original\ Confidential\ Balance}
$$

+ The allowance part of the confidential balance allows `_spender` to withdraw multiple times through calling `confidentialTransferFrom` until `_spender` does not call it any more or the value behind this part is 0.
    - Every time `_spender` calls `confidentialTransferFrom`, the value behind this part will be decreased by the value behind `_confidentialTransferValue`.
+ The left part remains as the new confidential balance of the caller&apos;s account.

If this function is called again, it:

+ merges the existing allowance part into the confidential balance of the caller&apos;s account; and then
+ overwrites the current allowance part with `_confidentialValue`.

Note:

+ Implementations can fully customize the proof system, (de)serialization strategies of `bytes`, and/or the business workflow. For example, when implementing &quot;Zether&quot; confidential token contracts, the `_confidentialValue` and accounts&apos; confidential balances will be encrypted homomorphically under ElGamal public keys, and `_proof` will consist of 3 parts to check:
    - `_confidentialValue` is well encrypted under public keys of caller&apos;s and `_spender`&apos;s;
    - The plaintext `value` behind `_confidentialValue` is non-negative;
    - The caller&apos;s confidential balance is actually enough to pay the plaintext `value` behind `_confidentialValue`.

###### `confidentialAllowance`
```solidity
function confidentialAllowance(address _owner, address _spender)
external view returns (bytes memory _confidentialValue)
```

Returns the allowance part that `_spender` is still allowed to withdraw from `_owner`.

##### Events

###### `ConfidentialTransfer`

```solidity
event ConfidentialTransfer(
  address indexed _spender,
  address indexed _from, 
  address indexed _to, 
  bytes _confidentialTransferValue
)
```

MUST trigger when tokens are transferred.

Specifically, if tokens are transferred through function `confidentialTransferFrom`, `_spender` address MUST be set to caller&apos;s; otherwise, it SHOULD be set to `0x0`.

A confidential token contract:

+ which creates new tokens SHOULD trigger a `ConfidentialTransfer` with the `_from` address set to `0x0` when tokens are minted;
+ which destroys existing tokens SHOULD trigger a `ConfidentialTransfer` with the `_to` address set to `0x0` when tokens are burned.

###### `ConfidentialApproval`

```solidity
event ConfidentialApproval(
  address indexed _owner,
  address indexed _spender,
  bytes _currentAllowancePart,
  bytes _allowancePart
)
```

MUST trigger on any successful call to `confidentialApprove(address,bytes,bytes)`.

## Rationale


### Optional Accessor of &quot;Confidential Total Supply&quot;

```solidity
function confidentialTotalSupply() external view returns (bytes memory)
```

Confidentiality of transfer amount makes it hard to support a field like `totalSupply()` in [SRC-20](/SIPS/sip-20). When it comes to token minting or burning, if every user in this contract can access `totalSupply()` as well as decrypt it, these users will know the actual token value minted or burned by comparing the `totalSupply()` before and after such operations, which means that confidentiality no longer exists.

Contract implementations can optionally support `confidentialTotalSupply()` by evaluating whether anti-money laundering (see next part) and audit are required. That would be much more plausible by allowing a small group of parties to know the plaintext total supply behind `confidentialTotalSupply()`.

### Anti-money Laundering and Audit
To support audit of confidential transactions and total supply, especially when such token issuers are banks or other financial institutions supervised by governments or monetary authorities, confidential transactions can be implemented without changing the `confidentialTransfer` method signature, by encoding more information into parameters.

For example, in a Zether-like implementation[^2], if token transfers are required to be audited, the `confidentialTransfer` caller encrypts transfer `value` redundantly under public keys of caller&apos;s, `to`&apos;s, and a group of auditors&apos;, which makes it possible for related parties to know the real `value` behind it exactly. So does `confidentialTotalSupply()`.

### Fat Token
A confidential-transactions-supported token can also implement [SRC-20](/SIPS/sip-20) at the same time.

Token accounts in such tokens can hold two kinds of balances. Such token contracts can optionally provide methods to hide [SRC-20](/SIPS/sip-20) plaintext balances into confidential balances, and vice versa, to reveal confidential balances back to [SRC-20](/SIPS/sip-20) plaintext balances.

[SRC-20](/SIPS/sip-20) interfaces will bring much more usability and utility to confidential-transaction-supported tokens, realizing general confidentiality in the meantime.

## Backwards Compatibility

No backward compatibility issues found.


## Security Considerations
To preserve confidentiality, implementations should avoid creating (minting) or destroying (burning) tokens with plaintext value parameters, since plaintext mint or burn flows may reveal sensitive amounts even if ordinary transfers remain confidential. Implementers should also ensure that any mint, burn, transfer, approval, and delegated transfer workflows use proof and encryption schemes that do not leak transfer values or balance information through calldata, events, or auxiliary state.

[^1]:
    ```csl-json
    {
      &quot;type&quot;: &quot;article&quot;,
      &quot;id&quot;: 1,
      &quot;author&quot;: [
        {
          &quot;family&quot;: &quot;Bünz&quot;,
          &quot;given&quot;: &quot;Benedikt&quot;
        },
        {
          &quot;family&quot;: &quot;Agrawal&quot;,
          &quot;given&quot;: &quot;Shashank&quot; 
        },
        {
          &quot;family&quot;: &quot;Zamani&quot;,
          &quot;given&quot;: &quot;Mahdi&quot; 
        },
        {
          &quot;family&quot;: &quot;Boneh&quot;,
          &quot;given&quot;: &quot;Dan&quot;
        }
      ],
      &quot;DOI&quot;: &quot;10.1007/978-3-030-51280-4_23&quot;,
      &quot;title&quot;: &quot;Zether: Towards Privacy in a Smart Contract World&quot;,
      &quot;original-date&quot;: {
        &quot;date-parts&quot;: [
          [2020, 2, 10]
        ]
      },
      &quot;URL&quot;: &quot;https://eprint.iacr.org/2019/191.pdf&quot;,
      &quot;custom&quot;: {
        &quot;additional-urls&quot;: [
          &quot;https://dl.acm.org/doi/abs/10.1007/978-3-030-51280-4_23&quot;
        ]
      }
    }
    ```

[^2]:
    ```csl-json
    {
      &quot;type&quot;: &quot;article&quot;,
      &quot;id&quot;: 2,
      &quot;author&quot;: [
        {
          &quot;family&quot;: &quot;Chen&quot;,
          &quot;given&quot;: &quot;Yu&quot;
        },
        {
          &quot;family&quot;: &quot;Ma&quot;,
          &quot;given&quot;: &quot;Xuecheng&quot;
        },
        {
          &quot;family&quot;: &quot;Tang&quot;,
          &quot;given&quot;: &quot;Cong&quot;
        },
        {
          &quot;family&quot;: &quot;Au&quot;,
          &quot;given&quot;: &quot;Man Ho&quot;
        }
      ],
      &quot;DOI&quot;: &quot;10.1007/978-3-030-58951-6_29&quot;,
      &quot;title&quot;: &quot;PGC: Decentralized Confidential Payment System with Auditability&quot;,
      &quot;original-date&quot;: {
        &quot;date-parts&quot;: [
          [2020, 9, 12]
        ]
      },
      &quot;URL&quot;: &quot;https://eprint.iacr.org/2019/319.pdf&quot;,
      &quot;custom&quot;: {
        &quot;additional-urls&quot;: [
          &quot;https://link.springer.com/chapter/10.1007/978-3-030-58951-6_29&quot;
        ]
      }
    }
    ```

## Copyright
Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Fri, 09 May 2025 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-7945</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-7945</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Facet-Based Diamonds</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #8153 - Facet-Based Diamonds&lt;/strong&gt; is in Last Call status. It is authored by Nick Mudge (@mudgen) and was originally created 2026-02-07. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/src-8153-facet-based-diamonds/27685&quot;&gt;https://sila-magicians.org/t/src-8153-facet-based-diamonds/27685&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

A diamond is a proxy contract that `delegatecall`s to multiple implementation contracts called facets. 

![Diagram showing how a diamond contract works](/assets/sip-8153/basic-diamond-diagram.svg)

Diamond contracts were originally standardized by [SRC-2535](/SIPS/sip-2535). This SRC builds on that foundation by defining a facet-based architecture in which facets self-describe their function selectors through a standardized introspection interface.

By moving selector discovery on-chain, this approach eliminates the need for off-chain selector management. As a result, diamond deployment and upgrades become simpler, more deterministic, and more gas efficient.

This SRC introduces a facet introspection function, `exportSelectors()`, which every facet MUST implement. This function returns the list of function selectors implemented by the facet, allowing a diamond to discover and register selectors on-chain during deployment or upgrade.

This SRC also defines facet-based events for adding, replacing, and removing facets.

Additionally, the SRC defines an optional `upgradeDiamond` function. This function uses `exportSelectors()` to automatically determine which selectors should be added, replaced, or removed when applying facet changes.

## Motivation

### Motivation for Diamond Contracts

&lt;img alt=&quot;Obligatory diamond&quot; src=&quot;../assets/sip-8153/diamond.svg&quot; width=&quot;17%&quot; align=&quot;right&quot;&gt;Through a single contract address, a diamond provides functionality from multiple implementation contracts (facets). Each facet is independent, yet facets can share internal functions and storage. This architecture allows large smart-contract systems to be composed from separate facets and presented as a single contract, simplifying deployment, testing, and integration with other contracts, off-chain software, and user interfaces.

By decomposing large smart contracts into facets, diamonds can reduce complexity and make systems easier to reason about. Distinct areas of functionality can be isolated, organized, tested, and managed independently.

Diamonds combine the single-address convenience of a monolithic contract with the modular flexibility of distinct, integrated contracts.

This architecture is well suited to **immutable** smart-contract systems, where all functionality is composed from multiple facets at deployment time and permanently fixed thereafter.

For upgradeable systems, diamonds enable incremental development: new functionality can be added, and existing functionality modified, without redeploying unaffected facets.

Additional motivation and background for diamond-based smart-contract systems can be found in [SRC-1538](/SIPS/sip-1538) and [SRC-2535](/SIPS/sip-2535).

### Motivation for this Standard

In the past, deploying and upgrading diamonds suffered from:

1. **High gas costs**
2. **Function selector management complexity**
   Deploying or upgrading a diamond requires assembling function selectors off-chain. Since common tooling (e.g., Hardhat, Foundry) does not natively manage diamond selectors, developers rely on custom scripts or third-party libraries to handle diamond &quot;plumbing&quot;.

This standard reduces gas costs and eliminates off-chain selector management:

* Diamonds become less expensive to deploy.
* Function selectors no longer need to be gathered off-chain.
* Standard deployment tools can be used without special diamond support.
* SRC-2535 introspection functions have simple implementations.

## Specification

### Terms
1. A **diamond** is a smart contract that routes external function calls to one or more implementation contracts, referred to as facets. A diamond is stateful: all persistent data is stored in the diamond&apos;s contract storage. A diamond implements the requirements in the [Implementation Requirements](#implementation-requirements) section.
2. A **facet** is a smart contract that defines one or more external functions. A facet is deployed independently, and one or more of its functions are added to one or more diamonds. A facet&apos;s functions are executed in the diamond&apos;s context via `delegatecall`, so reads/writes affect the diamond&apos;s storage. The term facet is derived from the diamond industry, referring to a flat surface of a diamond.
3. An **introspection function** is a function that returns information about the facets and/or functions used by a diamond or facet.
4. For the purposes of this specification, a **mapping** refers to a conceptual association between two items and does not refer to a specific implementation.

### Diamond Diagram

This diagram shows the structure of a diamond. 

It shows that a diamond has a mapping from function to facet and that facets can access the storage inside a diamond.

![Diagram showing structure of a diamond](/assets/sip-8153/functionFacetMapping.svg)

### Fallback

When an external function is called on a diamond, its fallback function is executed. The fallback function determines which facet to call based on the first four bytes of the calldata (known as the function selector) and executes the function from the facet using `delegatecall`.

A diamond&apos;s fallback function and `delegatecall` enable a diamond to execute a facet&apos;s function as if it were implemented by the diamond itself. The `msg.sender` and `msg.value` values do not change and only the diamond&apos;s storage is read and written to.

Here is an example of how a diamond&apos;s fallback function might be implemented:

```solidity
error FunctionNotFound(bytes4 _selector);

// Executes function call on facet using `delegatecall`.
// Returns function call return data or revert data.
fallback() external payable {
    // Get facet address from function selector
    address facet = selectorToFacet[msg.sig];
    if (facet == address(0)) {
        revert FunctionNotFound(msg.sig);
    }
    // Execute external function on facet using `delegatecall` and return any value.
    assembly {
        // Copy function selector and any arguments from calldata to memory.
        calldatacopy(0, 0, calldatasize())
        // Execute function call using the facet.
        let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
        // Copy all return data from the previous call into memory.
        returndatacopy(0, 0, returndatasize())
        // Return any return value or error back to the caller.
        switch result
        case 0 {revert(0, returndatasize())}
        default {return (0, returndatasize())}
    }
}
```
#### Function Not Found

If the fallback function cannot find a facet for a function selector, and there is no default function or other mechanism to handle the call, the fallback MUST revert with the error `FunctionNotFound(bytes4 _selector)`.

### Inspecting Diamonds

A diamond implementing this standard MUST implement the same introspection functions as defined in SRC-2535.
Specifically, these functions MUST be implemented:

```solidity
interface IDiamondInspect {
    struct Facet {
        address facetAddress;
        bytes4[] functionSelectors;
    }

    /// @notice Gets all facet addresses and their four byte function selectors.
    /// @return facets_ Facet
    function facets() external view returns (Facet[] memory facets_);

    /// @notice Gets all the function selectors supported by a specific facet.
    /// @param _facet The facet address.
    /// @return facetFunctionSelectors_
    function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);

    /// @notice Get all the facet addresses used by a diamond.
    /// @return facetAddresses_
    function facetAddresses() external view returns (address[] memory facetAddresses_);

    /// @notice Gets the facet that supports the given selector.
    /// @dev If facet is not found return address(0).
    /// @param _functionSelector The function selector.
    /// @return facetAddress_ The facet address.
    function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
}
```

Typically, these functions are implemented in a facet and the facet is added to diamonds.

### Inspecting Facets

Each facet MUST implement the following pure introspection function:

```solidity
interface IFacet {
    function exportSelectors() external pure returns (bytes memory selectors);
}
```

`exportSelectors()` returns a `bytes` array containing one or more 4-byte function selectors. The returned `bytes` array length MUST be a multiple of 4, and each 4-byte chunk is a selector. The function MUST NOT return a specific selector more than once.

The `bytes` array contains selectors of functions implemented by the facet that are intended to be added to a diamond.

This enables a diamond to discover selectors directly from facets at deployment or upgrade time. A diamond calls `exportSelectors()` on each facet to determine which selectors to add, replace, or remove.

Selector gathering is therefore no longer an off-chain responsibility.

This also means diamonds implementing this SRC are **facet-based** rather than **function-based**. Deployment and upgrades operate on facets.

### Facet-Based Events

This SRC replaces SRC-2535&apos;s function-based events with facet-based events. 

When facets are added, replaced, or removed, the diamond MUST emit the following events:

```solidity
 /**
  * @notice Emitted when a facet is added to a diamond.
  * @dev The function selectors this facet handles can be retrieved by calling
  *      `IFacet(_facet).exportSelectors()`
  *
  * @param _facet The address of the facet that handles function calls to the diamond.
  */
event FacetAdded(address indexed _facet);

/**
 * @notice Emitted when an existing facet is replaced with a new facet.
 * @dev
 * - Selectors that are present in the new facet but not in the old facet are added to the diamond.
 * - Selectors that are present in both the new and old facet are updated to use the new facet.
 * - Selectors that are not present in the new facet but are present in the old facet are removed from
 *   the diamond.
 *
 * The function selectors handled by these facets can be retrieved by calling:
 * - `IFacet(_oldFacet).exportSelectors()`
 * - `IFacet(_newFacet).exportSelectors()`
 *
 * @param _oldFacet The address of the facet that previously handled function calls to the diamond.
 * @param _newFacet The address of the facet that now handles function calls to the diamond.
 */
event FacetReplaced(address indexed _oldFacet, address indexed _newFacet);

/**
 * @notice Emitted when a facet is removed from a diamond.
 * @dev The function selectors this facet handles can be retrieved by calling
 *      `IFacet(_facet).exportSelectors()`
 *
 * @param _facet The address of the facet that previously handled function calls to the diamond.
 */
event FacetRemoved(address indexed _facet);
```

Block explorers and other tooling can obtain the function selectors for any of the facets referenced by these events by calling `exportSelectors()` on the facet address.

### Optional Events

#### Recording Non-Fallback `delegatecall`s

This event is OPTIONAL, except `upgradeDiamond` functions MUST emit it as specified in this standard.

This event can be used to record `delegatecall`s made by a diamond.

This event MUST NOT be emitted for `delegatecall`s made by a diamond&apos;s fallback function when routing calls to facets. It is only intended for `delegatecall`s made by functions in facets or a diamond&apos;s constructor.

This event enables tracking of changes to a diamond&apos;s contract storage caused by `delegatecall` execution.

```solidity
/**
* @notice Emitted when a diamond&apos;s constructor or function from a
*         facet makes a `delegatecall`. 
* 
* @param _delegate         The contract that was the target of the `delegatecall`.
* @param _delegateCalldata The function call, including function selector and 
*                          any arguments.
*/
event DiamondDelegateCall(address indexed _delegate, bytes _delegateCalldata);
```

#### Diamond Metadata

This event is OPTIONAL, except `upgradeDiamond` functions MUST emit it as specified in this standard.

This event can be used to record versioning or other information about diamonds.

It can be used to record information about diamond upgrades.

```solidity
/**
* @notice Emitted to record information about a diamond.
* @dev    This event records any arbitrary metadata. 
*         The format of `_tag` and `_data` are not specified by the 
*         standard.
*
* @param _tag   Arbitrary metadata, such as a release version.
* @param _data  Arbitrary metadata.
*/
event DiamondMetadata(bytes32 indexed _tag, bytes _data);
```

### Implementation Requirements

A facet-based diamond MUST implement the following:

1. **Diamond Structure**
   - A `fallback()` function.
2. **Function Association**
   - It MUST associate function selectors with facet addresses.
3. **Function Execution**
   - When an external function is called on a diamond:
     - The diamond&apos;s fallback function is executed. 
     - The fallback function MUST find the facet associated with the function selector.
     - The fallback function MUST execute the function on the facet using `delegatecall`.
     - If no facet is associated with the function selector, the diamond MAY execute a default function or apply another handling mechanism.
     - If no facet, default function, or other handling mechanism exists, execution MUST revert with the error `FunctionNotFound(bytes4 _selector)`.
4. **Events**
   - The following events MUST be emitted:
     - `FacetAdded` — when a facet is added to a diamond.
     - `FacetReplaced` — when a facet is replaced with a different facet.
     - `FacetRemoved` — when a facet is removed from a diamond.
5. **Diamond Introspection**
   - A diamond MUST implement the following introspection functions:
     - `facets()`
     - `facetFunctionSelectors(address _facet)`
     - `facetAddresses()`
     - `facetAddress(bytes4 _functionSelector)`
6. **Facet Introspection**
   - Each facet MUST implement the `exportSelectors()` function, which returns a `bytes` array.


### `receive()` Function

A diamond MAY have a `receive()` function.

### `upgradeDiamond` Function

Implementing `upgradeDiamond` is OPTIONAL.

This function is specified for interoperability with tooling (e.g., GUIs and command-line tools) so that upgrades can be executed with consistent and predictable behavior.

`upgradeDiamond` adds, replaces, and removes any number of facets in a single transaction. It can also optionally execute a `delegatecall` to perform initialization or state migration.

The `upgradeDiamond` function works as follows:

#### Adding a Facet

1. Call `exportSelectors()` on the facet to obtain its function selectors. 
2. Add each selector to the diamond, mapping it to the facet address.

#### Replacing a Facet

1. Call `exportSelectors()` on the old facet to obtain its function selectors.
2. Call `exportSelectors()` on the new facet to obtain its function selectors.
3. For selectors present in the new facet but not the old facet: add them.
4. For selectors present in both: replace them to point to the new facet.
5. For selectors present in the old facet but not the new facet: remove them.

#### Removing a Facet
1. Call `exportSelectors()` on the facet to obtain its function selectors.
2. Remove each selector from the diamond.

#### Errors and Types

```solidity
/**
 * @notice The upgradeDiamond function below detects and reverts
 *         with the following errors.
 */
error NoSelectorsForFacet(address _facet);
error NoBytecodeAtAddress(address _contractAddress);
error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector);
error CannotRemoveFacetThatDoesNotExist(address _facet);
error CannotReplaceFacetWithSameFacet(address _facet);
error FacetToReplaceDoesNotExist(address _oldFacet);
error DelegateCallReverted(address _delegate, bytes _delegateCalldata);
error ExportSelectorsCallFailed(address _facet);

/**
 * @dev This error means that a function to replace exists in a
 *      facet other than the facet that was given to be replaced.
 */
error CannotReplaceFunctionFromNonReplacementFacet(bytes4 _selector);

/**
 * @notice This struct is used to replace old facets with new facets.
 */
struct FacetReplacement {
    address oldFacet;
    address newFacet;
}
```

#### Function Signature

```solidity
/**
 * @notice Upgrade the diamond by adding, replacing, or removing facets.
 *
 * @dev
 * Facets are added first, then replaced, then removed.
 *
 * These events are emitted to record changes to facets:
 * - `FacetAdded(address indexed _facet)`
 * - `FacetReplaced(address indexed _oldFacet, address indexed _newFacet)`
 * - `FacetRemoved(address indexed _facet)`
 *
 * If `_delegate` is non-zero, the diamond performs a `delegatecall` to
 * `_delegate` using `_delegateCalldata`. The `DiamondDelegateCall` event is
 *  emitted.
 *
 * The `delegatecall` is done to alter a diamond&apos;s state or to
 * initialize, modify, or remove state after an upgrade.
 *
 * However, if `_delegate` is zero, no `delegatecall` is made and no
 * `DiamondDelegateCall` event is emitted.
 *
 * If _tag is non-zero or if _metadata.length &gt; 0 then the
 * `DiamondMetadata` event is emitted.
 *
 * @param _addFacets        Facets to add.
 * @param _replaceFacets    (oldFacet, newFacet) pairs, to replace old with new.
 * @param _removeFacets     Facets to remove.
 * @param _delegate         Optional contract to delegatecall (zero address to skip).
 * @param _delegateCalldata Optional calldata to execute on `_delegate`.
 * @param _tag              Optional arbitrary metadata, such as release version.
 * @param _metadata         Optional arbitrary data.
 */
function upgradeDiamond(
    address[] calldata _addFacets,
    FacetReplacement[] calldata _replaceFacets,
    address[] calldata _removeFacets,
    address _delegate,
    bytes calldata _delegateCalldata,
    bytes32 _tag,
    bytes calldata _metadata
) external;
```
The `upgradeDiamond` function MUST adhere to the following requirements:

&gt; Definitions of events and custom errors referenced below are given earlier in this standard.

1. **Inputs**
   - `_addFacets` array of facet addresses to add.
   - `_replaceFacets` array of (`oldFacet`, `newFacet`) pairs.
   - `_removeFacets` array of facet addresses to remove.

2. **Execution Order**
   1. Add facets
   2. Replace facets
   3. Remove facets

3. **Event Emission**
   - Every change to a facet MUST emit exactly one of:
     - `FacetAdded`
     - `FacetReplaced`
     - `FacetRemoved`

4. **Error Conditions**
   - The implementation MUST detect and revert with the specified error when:
     - Adding a selector that already exists: `CannotAddFunctionToDiamondThatAlreadyExists`.
     - Removing a facet that does not exist: `CannotRemoveFacetThatDoesNotExist`.
     - Replacing a facet with itself: `CannotReplaceFacetWithSameFacet`.
     - Replacing a facet that does not exist: `FacetToReplaceDoesNotExist`.
     - Replacing a selector that exists in the diamond but is mapped to a facet different than the facet being replaced: `CannotReplaceFunctionFromNonReplacementFacet`.

5. **Facet Validation**
   - If any facet address contains no contract bytecode, revert with `NoBytecodeAtAddress`.
   - If `exportSelectors()` is missing, reverts, or cannot be called successfully, revert with `ExportSelectorsCallFailed`.
   - If `exportSelectors()` returns zero selectors, revert with `NoSelectorsForFacet`.

6. **Delegate Validation**
   - If `_delegate` is non-zero but contains no bytecode, revert with `NoBytecodeAtAddress`.

7. **Delegatecall Execution**
   - If `_delegate` is non-zero, the diamond MUST `delegatecall` `_delegate` with `_delegateCalldata`.
   - If the `delegatecall` fails and returns revert data, the diamond MUST revert with the same revert data.
   - If the `delegatecall` fails and returns no revert data, revert with `DelegateCallReverted`.
   - If a `delegatecall` is performed, the diamond MUST emit the `DiamondDelegateCall` event.
   - `_delegateCalldata` MAY be empty. If empty, the `delegatecall` executes with no calldata.

8. **Metadata Event**
   - If `_tag` is non-zero or `_metadata.length &gt; 0`, the diamond MUST emit the `DiamondMetadata` event.

After adding, replacing, or removing facets, the diamond MAY perform a `delegatecall` to initialize, migrate, or clean up state.

It is also valid to call `upgradeDiamond` solely to perform a `delegatecall` (i.e., without adding, replacing, or removing any facets).

To skip an operation, supply an empty array for its parameter (for example, `new address[](0)` for `_addFacets`).

## Rationale

### Eliminating Selector Management

To deploy a facet-based diamond implementing this SRC, the deployer provides an array of facet addresses to the diamond constructor. The constructor calls `exportSelectors()` on each facet and registers those selectors in the diamond.

Because facets self-describe their selectors, deployers no longer need to gather selectors off-chain or depend on specialized selector tooling.

### Reducing Deployment Gas Costs

#### Reducing Calldata

In a non-facet-based diamond, selectors are typically passed to the constructor as one or more `bytes4[]` arrays. These arrays are paid for in calldata and then copied into memory, incurring additional gas.

In a facet-based diamond, only facet addresses are passed to constructors. The diamond calls `exportSelectors()` on each facet to obtain selectors on-chain, avoiding calldata costs for selector lists. While calling `exportSelectors()` introduces some overhead, non-facet-based diamonds typically perform code-existence checks (e.g., `extcodesize`) on facet addresses anyway, incurring the cold account access gas cost.

#### Reducing Storage

In a **non-facet-based diamond**, function selectors are stored directly for introspection, typically in a `bytes4[] selectors` array (or an equivalent structure). Because a storage slot is 32 bytes, each slot can hold up to eight `bytes4` selectors. As more functions are added, additional storage slots are required, so storage usage grows linearly with the number of selectors.

In a **facet-based diamond**, introspection data can be stored **per facet instead of per function**. Each facet only needs a single representative selector. This means one 32-byte storage slot can represent up to eight facets, regardless of how many function selectors each facet implements. Storage usage therefore grows with the number of facets, not the number of functions.

Alternatively, a facet-based diamond can be implemented as a **linked list of facets**. With this design, introspection requires a **single 32-byte storage slot**, while supporting any number of facets and any number of selectors per facet.

### `exportSelectors()` Function Return Value

`exportSelectors()` returns `bytes` rather than `bytes4[]` for two reasons:

#### `bytes4[]` Wastes Memory

Each element of a `bytes4[]` array occupies 32 bytes in memory, but only 4 bytes are meaningful. This wastes 87.5% of allocated memory, increasing gas costs. Packing selectors into `bytes` reduces memory overhead.

#### Simple Syntax For Facets

Facets can implement `exportSelectors()` concisely using Solidity&apos;s built-in function `bytes.concat`. Example:

```solidity
function exportSelectors() external pure returns (bytes memory) {
    return bytes.concat(
        this.facetAddress.selector,
        this.facetFunctionSelectors.selector,
        this.facetAddresses.selector,
        this.facets.selector
    );
}
```

The diamond can traverse the returned bytes and extract selectors efficiently.

### Diamond Upgrades

The upgrade function specified by this standard is optional.

This means a couple of things:

#### 1. Diamonds Can Be Immutable

A Diamond does not have to have an upgrade function.

- A diamond can be fully constructed within its constructor without adding any upgrade function, making it immutable upon deployment.

- A large immutable diamond can be built using well organized facets.

- A diamond can initially be upgradeable, and later made immutable by removing its upgrade function.

#### 2. You Can Create Your Own Upgrade Functions

You can design and create your own upgrade functions and remain compliant with this standard. All that is required is that you emit the appropriate add/replace/remove events specified in the [Facet-Based Events section](#facet-based-events), and that the introspection functions defined in the [Inspecting Diamonds section](#inspecting-diamonds) and the [Inspecting Facets section](#inspecting-facets) continue to exist and accurately return function and facet information.

### Runtime Gas Considerations

Routing calls via `delegatecall` introduces a small amount of gas overhead. In practice, this cost is mitigated by several architectural and tooling advantages enabled by diamonds:

1. **Optional, gas-optimized functionality**  
   By structuring functionality across multiple facets, diamonds make it straightforward to include specialized, gas-optimized features without increasing the complexity of core logic.  
   For example, an [SRC-721](/SIPS/sip-721) diamond may implement batch transfer functions in a dedicated facet, improving both gas efficiency and usability while keeping the base SRC-721 implementation simple and well-scoped.

2. **Reduced external call overhead**    
   Some contract architectures require multiple external calls within a single transaction. By consolidating related functionality behind a single diamond address, these interactions can execute internally with shared storage and shared authorization, reducing gas costs from external calls and repeated access-control checks.  

3. **Selective optimization per facet**  
   Because facets are compiled and deployed independently, they may be built with different compiler optimizer settings. This allows gas-critical facets to use aggressive optimization configurations to reduce execution costs, without increasing bytecode size or compilation complexity for unrelated functionality.

### Storage Layout

Diamonds and facets need to use a storage layout organizational pattern because Solidity&apos;s default storage layout doesn&apos;t support proxy contracts or diamonds. The storage layout technique or pattern to use is not specified in this SRC. However, examples of storage layout patterns that work with diamonds are [SRC-8042 Diamond Storage](/SIPS/sip-8042) and [SRC-7201 Namespaced Storage Layout](/SIPS/sip-7201).

### Facets Sharing Storage &amp; Functionality

Facets are separately deployed, independent units, but can share state and functionality in the following ways:

- Facets can share state variables by using the same structs at the same storage positions. 
- Facets can share internal functions by importing them or inheriting contracts. 

### On-chain Facets can be Reused and Composed

A deployed facet can be used by many diamonds.

It is possible to create and deploy a set of facets that are reused by different diamonds.

The ability to use the same deployed facets for many diamonds has the potential to reduce development time, increase reliability and security, and reduce deployment costs.

It is possible to implement facets in a way that makes them usable/composable/compatible with other facets. 

## Backwards Compatibility

Diamonds implementing this SRC have the same introspection functions as SRC-2535 diamonds, so they are compatible with SRC-2535 tooling that relies on these functions.

This SRC breaks compatibility with SRC-2535 events and upgrades.

Facets deployed for SRC-2535 diamonds cannot be used with SRC-8153 diamonds if they do not have the `exportSelectors()` function.

## Security Considerations

### Arbitrary Execution with `upgradeDiamond`

The `upgradeDiamond` function allows arbitrary execution with access to the diamond&apos;s storage (through delegatecall). Access to this function must be restricted carefully.

### Use Only Trusted and Verified Facets

Only trusted and verified facets should be added to facet-based diamonds.

`exportSelectors()` MUST be `pure` and should not contain logic that varies the returned bytes. Facets should be immutable so returned selectors cannot change over time.

If a facet&apos;s `exportSelectors()` output changes, upgrades that rely on it may add/remove/replace the wrong selectors and corrupt diamonds.

### Upgrade Integrity Checks

The specified `upgradeDiamond` behavior prevents a number of upgrade mistakes. Upgrades revert when:

- A facet is added that already exists in the diamond.
- A facet is replaced or removed that does not exist in the diamond.
- A selector is added that already exists in the diamond.
- A selector is replaced that exists in the diamond but is mapped to a different facet than the facet being replaced.
- A facet address contains no bytecode.
- A facet does not implement `exportSelectors()` successfully.
- A facet provides zero selectors.
- A facet is replaced with itself (same contract address).

Selector collisions (two different signatures with the same 4-byte selector) are handled as &quot;selector already exists&quot; and are therefore prevented.

### Do Not Self Destruct
Use of selfdestruct in a facet is heavily discouraged. Misuse of it can delete a diamond or a facet.

### Transparency

A diamond emits an event every time a facet is added, replaced or removed. Source code can be verified. This enables people and software to monitor changes to a diamond. 

Security and domain experts can review a diamond&apos;s upgrade history.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Sat, 07 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-8153</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-8153</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
      
      <item>
        <title>Modular Dispatch Proxies</title>
        <description>
        &lt;p&gt;&lt;strong&gt;SIP #8167 - Modular Dispatch Proxies&lt;/strong&gt; is in Last Call status. It is authored by William Morriss (@wjmelements), Radek Svarz (@radeksvarz) and was originally created 2026-02-16. It is in the SRC category of type Standards Track. Please review and note any changes that should block acceptance.&lt;/p&gt;
        
          &lt;p&gt;The author has requested that discussions happen at the following URL: &lt;a href=&quot;https://sila-magicians.org/t/src-8167-modular-dispatch-proxies/27781&quot;&gt;https://sila-magicians.org/t/src-8167-modular-dispatch-proxies/27781&lt;/a&gt;&lt;/p&gt;
        
        &lt;hr /&gt;
        ## Abstract

This proposal standardizes dispatch proxies, which dispatch calls to logic modules, called delegates, according to function selector.
A modular proxy architecture facilitates upgrades, extensions, and hardening, while working around codesize limits.
This minimal standard interface allows tooling to discover the ABI of these proxies and examine their upgrade history.

## Motivation

Proxy contracts utilizing `delegatecall` are widely used for both code sharing and upgradeability.
Most common proxies forward calldata to a single implementation contract.
Sometimes the implementation address is hardcoded, a pattern used by cloning factories to reduce deployment costs, and sometimes the implementation address is mutable, a pattern used by upgradeable proxies.
However, monolithic proxy architectures can bump into codesize limits.
Additionally, replacing the implementation of an entire contract at once can be riskier than smaller, more incremental changes.

### Shared Logic Modules

Many contracts share common code for things like tokens but cannot share their entire implementation because of their own unique characteristics.
For example, two tokens might share their balance logic and transfer interface but differ in their name metadata and monetary policy.
With a monolithic architecture, these differences require two separate contracts.
With a logic module architecture, they can share a standardized token implementation but customize their metadata and monetary policy.

### Extension

Sometimes new standards arise that provide new functionality or guarantees.
For example, a popular token interface extension might arise to provide a new and better method for modifying allowances.
With a monolithic architecture, token implementations must be wrapped or wholly replaced to support the new method.
With logic modules, the interface could be extended with a new module to support the new method.

Modular designs are also appropriate for personal smart accounts such as [SIP-7702](/SIPS/sip-7702) EOAs.
User accounts could install features such as DEX-specific callbacks without temporarily disabling other functionality.

### Upgrade

Monolithic proxy architectures require replacing the entire implementation during an upgrade.
Such upgrades batch changesets but introduce risk and are difficult to test and verify.
Modular dispatch proxies can still atomically batch upgrades, but their modular architecture allows incremental improvements and fixes without unintentionally breaking unrelated components.

### Hardening

Upgradeable dispatch proxies can be permanently hardened into immutable systems by uninstalling the upgrade methods.

### Standardization

A standard interface for the modular proxy architecture can help tools, user interfaces, and indexers determine the ABI of these proxies.
Such systems may also want to surface the full upgrade history of these proxies to facilitate investigation.

## Specification

A modular dispatch proxy MUST use `delegatecall` to relay the entire calldata to the delegate corresponding to the first four bytes of the calldata.

```solidity
interface ISRC8167 {
    // RECOMMENDED
    // Emitted when assigning a delegate logic module to a selector
    // An address(0) delegate signals removal
    event SelectorDelegated(bytes4 indexed selector, address indexed delegate);

    // REQUIRED
    // Returns the delegate for the selector, using address(0) for function not found
    function implementation(bytes4 selector) external view returns (address);

    // REQUIRED
    // Surfaces the ABI
    // SHOULD return all function selectors with implementations
    function selectors() external view returns (bytes4[] memory);

    // RECOMMENDED
    // If the delegate for that selector is not set, the proxy SHOULD revert, and with FunctionNotFound.
    error FunctionNotFound(bytes4 selector);
}
```

`ISRC8167` functions SHOULD be implemented by delegates rather than in the proxy.

A modular dispatch proxy constructor SHOULD configure at least one delegate.

## Rationale

### `bytes4 selector`

The most widely-supported ABI is Solidity&apos;s 4-byte ABI, which uses the first four bytes of calldata, called the selector, to dispatch functions.
The dispatch proxy also uses those same four bytes to dispatch function calls to their delegate.

### `implementation(bytes4)`

While implementations can be discovered with `sil_getStorageAt`, a common interface can support a variety of possible storage layouts and implementations.

This function&apos;s naming is consistent with monolithic proxies, but with a selector parameter.

### `selectors()`

This is a minimal function to surface ABI to tools.
While selectors are ambiguous, they can be resolved if their delegate has a verified ABI.
Together, these steps produce the ABI of the proxy:
1. For each `selector` in `selectors()`, query `implementation(selector)`.
2. For each unique implementation, check if its code is verified. If verified, retrieve the ABI. If not, allow the user to supply the missing ABI.
3. Identify the functions supported by the proxy by matching its selectors with their implementation&apos;s ABI.

Although selectors are also retrievable by querying `SelectorDelegated` events, the `selectors` function provides a way to get this information without access to the logs.
Log queries can be slow without a database index.

While a packed encoding would reduce memory allocation, an array of `bytes4` is the simplest for tooling to decode.
It is anticipated that this method will primarily be used by tooling.

### Upgrades

This standard does not specify an upgrade function.
Other standards could extend this one with versioning frameworks for atomic batch upgrades.

### Storage Layout

This standard does not specify a storage layout.
Other standards could suggest patterns to protect against storage collisions and other mistakes.

## Backwards Compatibility

This standard improves upon [SRC-2535](/SIPS/sip-2535) in the following ways:

1. Removal of diamond jargon.
2. Fewer and simpler introspection functions.
3. Simpler upgrade event.

Existing upgradeable monolithic proxies can upgrade to this standard using the following upgrade plan:
1. Upgrade to an implementation with a method to populate the selector delegate mapping.
2. Populate the selector delegate mapping for all methods in the ABI plus the introspection functions.
3. Set the implementation to a dispatch proxy using the populated selector delegate mapping.

Existing modular proxies can upgrade to this standard by adding the introspection functions and optionally emitting a `SelectorDelegated` event for every installed function.

## Reference Implementation

The reference implementation contains three files.

- The [interface](/assets/sip-8167/ISRC8167.sol)
- A namespace-based [storage layout](/assets/sip-8167/ProxyStorageBase.sol)
- A [proxy implementation](/assets/sip-8167/Proxy.sol) with delegates for inspection and administration

## Security Considerations

### Access control

Upgrade functions should have some form of access control.
Access control designs are outside the scope of this standard.

### Avoid self-destruct

Delegates should not self-destruct.
If a delegate can self-destruct, it can break proxies that use it.

### Storage Layout

Proxy upgrades must take care not to shift storage indices because this corrupts contract data.

Delegates should be designed to minimize the risk of storage layout overlap between them.
There are two known approaches to protect storage layouts against such collisions.

The first is to define a single shared proxy storage layout in a common superclass inherited by all of the proxy&apos;s delegates.
Such subclasses should not declare additional storage.

The second is to use storage namespaces, such as [SRC-7201](/SIPS/sip-7201) and [SRC-8042](/SIPS/sip-8042).
This approach is appropriate for shared libraries.

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).

      </description>
        <pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://sips.sila.org//SIPS/sip-8167</link>
        <guid isPermaLink="true">https://sips.sila.org//SIPS/sip-8167</guid>
      </item>
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
  </channel>
</rss>
