Drawing from our audit of Airswift's SCF, we discuss part of Soroban's security model and showcase common vulnerabilities. SCF, for "Supply Chain Financing", is the DeFi product developed by Airswift that "optimizes funds flow" between buyers and suppliers. It is developed on Stellar's smart contract platform: Soroban. Airswift mandated Quarkslab for an audit of their smart contracts, with support from the Stellar Development Foundation. In this blog post, we present the results of this audit, and share common pitfalls to avoid on Soroban.

Introduction

Airswift's SCF is a financing solution tackling supply chain inefficiencies. It enables buyers to optimize their working capital, while securing the funds of their suppliers.

Airswift developed two variations of their product: one limited to and for the Argentinian market, and a generic one for the rest of the world. The general idea is to tokenize funded buyers orders, so they can be lent and borrowed freely on-chain.

Airswift is responsible for emitting certificates in the form of (divisible) NFTs, that are then funded by the buyers. Depending on the implementation, it can be split into parts or not, then transferred like a regular NFT. Airswift also provides a lending and borrowing platform for these certificates.

The Stellar ecosystem provides the Soroban environment. It is a smart contract platform that was audited by Quarkslab and introduced as a new feature to the mainnet. This platform includes the smart contract environment, a Rust SDK, a CLI, and an RPC server.

The goal of this audit was to assess the security of the two Soroban implementations of Airswift's SCF solution. The full audit report is available on the Airswift website.

Scope

The scope of this audit included six smart contracts (2 utility, plus 2 for each implementation), available in the Airswiftio/SCF GitHub repository:

  • the Soroban equivalent to an ERC-20,1 to be able to interact with stablecoins;
  • a smart contract deployer;
  • the NFT representing certificates; and
  • a pool to lend and borrow the certificates.

Findings

The table below summarizes the findings of the audit. Although there is some overlap between implementations, we decided to duplicate issues when they were present in both smart contracts, so that we could track their resolution separately.

A total of 33 (27 unique) issues were found, including 3 unique with a critical severity rating and 4 unique rated high.

We found that Airswift's original codebase fell for common security pitfalls, which are discussed below.

ID Title Severity Perimeter
CRIT-1 Approvals are stored in Instance storage Critical argentina_pledge approval
CRIT-2 Approvals are not revoked upon regular transfer Critical argentina_pledge approval
CRIT-3 Approvals are stored in Instance storage Critical scf_soroban approval
CRIT-4 Approval is not reset during token transfer Critical scf_soroban transfer
CRIT-5 Uncapped supply of token leads to loss of funds Critical scf_soroban split
HIGH-1 Borrower's TC may never be transferred back after payoff, leading to loss of funds High argentina_pledge loan
HIGH-2 Loan offer creation can be censored by front-running High argentina_pool loan
HIGH-3 Offer creation accepts untrusted pool_tokens High scf_pool
HIGH-4 Tokenized certificate owner can split before accepting an offer High scf_pool
MED-1 Approvals cannot be revoked Medium argentina_pledge approval
MED-2 Untrusted contract call in accept_load_offer Medium argentina_pool loan
MED-3 Token approval can't be deleted Medium scf_soroban approval
MED-4 Offer creation accepts non-existing tokenized certificate contracts and identifiers Medium scf_pool
MED-5 User may be censored through front-running Medium scf_pool
LOW-1 Unbounded storage of DataKey::FileHashes(i128) Low argentina_pledge storage
LOW-2 Mismatched storage type of DataKey::Owner(i128) Low argentina_pledge storage
LOW-3 Mismatched storage type of DataKey::Approval(ApprovalKey::ID(i128)) Low argentina_pledge storage
LOW-4 Too small type for TC amount Low argentina_pledge mint
LOW-5 Redeem time's validity is not checked at mint time Low argentina_pledge mint
LOW-6 Split may be smaller than 10% of the root's total_amount Low scf_soroban split
LOW-7 Uncapped number of verifiable credentials per token Low scf_soroban add_vc
INFO-1 Warnings emitted during the compilation Info argentina_pledge
INFO-2 Improper type for TC IDs Info argentina_pledge
INFO-3 Warnings emitted during the compilation Info argentina_pledge
INFO-4 Unused DataKey variants Info argentina_pool
INFO-5 Fixed-point variable has limited resolution Info argentina_pool loan payoff
INFO-6 Bad public variable name Info argentina_pool
INFO-7 Superfluous field in Loan Info argentina_pool storage
INFO-8 Superfluous liquidity token Info argentina_pool token
INFO-9 Storage keys are not standardized Info scf_soroban storage
INFO-10 Unused data key variants Info scf_soroban storage
INFO-11 The end_time can be configured to a past timestamp Info scf_soroban initialize
INFO-12 Verifiable credential can be any format Info scf_soroban VC

All vulnerabilities have been addressed, most by fixing them. MED-4 will be mitigated in the front-end, and 2 lower severity issues have been acknowledged without a planned fix.

Discussion

In light of these findings, we selected three issues that can commonly be found in other projects, and discuss them here.

Authorization model

The developed NFT contract lets users authorize a third party to transfer their tokens.

This is a common paradigm on EVM chains: when interacting with a DeFi protocol, the user needs to authorize the protocol to use their tokens. To do so, they first need to perform an "approve" transaction on the token before interacting with the protocol.

This flow is not necessary on Soroban, since the user signing the transaction can add an authorization for subcontract calls.2 In this case, the user approves the token transfer at the same time as they interact with the DeFi protocol.

Unless required by a more obscure use-case not solved by Soroban's authorization framework, we recommend not to include EVM style authorization methods to DeFi protocols. Indeed, while not inherently unsafe, they carry additional risk for end users.34 If approvals are still needed, developers should use Temporary storage, so they can expire when unused.

Airswift followed our recommendation and removed the superfluous mechanism.

Superfluous token

It is common for DeFi projects to emit their own tokens for their protocols. Reasons to do so are numerous, and are the subject of vivid debates on the utility of one or the other. We won't discuss this here, and only remind developers that creating a token is not always necessary.

During this audit, we found a clear example of such a superfluous token: it can be minted or burnt at a 1:1 rate with a configured stablecoin, is only necessary to interact with the protocol, and it cannot be upgraded. In this state, users can very well

  1. swap their stablecoin for the custom token,
  2. use it in the protocol, and
  3. swap back whenever they receive the custom token.

The users have no incentive to keep or use the token elsewhere, and its value is exactly tied to that of the chosen stablecoin.

From the protocol's point of view, using a custom token or the stablecoin directly changes nothing, so the custom token only acts as a transparent proxy for the stablecoin, and adds friction to interact with the protocol.

In such cases, we recommend removing the token entirely and interacting directly with the configured stablecoin.

Uncooperative users

SCF designed a flow for certificate loans, in which the loan's state advances as users call the corresponding functions. Such state-based operational flow can be seen in many decentralized applications, and developers need to make sure that some users cannot lock the process in a state that is detrimental to someone else, even if it does not benefit anyone.5

As an example, let's take the original argentinian_pledge flow:

A state machine can transition from 'Pending', to 'Active', to 'Paid', to 'Close'. It can also jump directly from 'Pending' to 'Close'.

Original flow of loans in the Argentinian implementation


Here, a malicious or uncooperative creditor could for example choose not to call the close_loan function, preventing the borrower who paid their debt from getting the certificate. The borrower ends up with neither the certificate nor the borrowed value as the loan is stuck in the Paid state, while the creditor keeps the certificate but does not get the money it lent back. The solution implemented by Airswift was to

  • make the smart contract own the certificate on behalf of the creditor, so that they cannot keep the certificate to themselves;
  • allow the administrator to force default the loan, in order to give the creditor full ownership of the certificate;
  • merge the operations of payoff_loan and close_loan.

A state machine can transition from 'Pending', to 'Active', to 'Close'. It can also jump directly from 'Pending' to 'Close', or from 'Active' to 'Defaulted'.

Fixed flow of loans in the Argentinian implementation


Conclusion

We found several issues in our audit of Airswift's Supply Chain Financing implementations, that are now resolved. We also highlighted common pitfalls in smart contract design and development on Soroban.

Overall, working with the Airswift team was a great experience. They were very attentive to Quarkslab's proposed remediations, and committed to enhancing the project's security.

Going forward, we strive to continue pursuing our mission of advancing security in the Web3 ecosystem.


If you would like to learn more about our security audits and explore how we can help you, get in touch with us!