Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions kip-0009/kip-0009.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
KIP: 0009
Title: restricted-fungible-v1
Author: Stuart Popejoy @sirlensalot
Status: Draft
Type: Standard
Category: Chainweb
Created: 2021-02-02
---

## Abstract

Define and implement a Pact interface to specify restrictions on transfer of a fungible token.

## Motivation

As seen in ERC-1404, certain use cases require restriction of transfer of a fungible token. This
interface is modeled closely on ERC-1404 as an open-ended "descriptive" API that implies the
restriction must be honored in `transfer` and related functions.

## Rationale

### One function to rule them all

The only particularity worth mentioning is the simplification from ERC-1404 from two functions to
one. There is no need for a descriptive function as the equivalent of `detectTransferRestriction`
can return a `string` in Pact (Solidity lacks mechanisms for string comparison), and the lack of
a standard on what integer values in ERC-1404 might mean.

### Specific to `fungible-v1`

As opposed to a generic `restricted-v1`, that could e.g. apply to non-fungible tokens like KIP-0008/`non-fungible-v1`,
this follows ERC-1404 closely in including the _amount_ in the restriction API, as opposed to focusing
only on accounts. This makes it incompatible with `non-fungible-v1` which uses a `string` identifier
for NFTs.

Note that Pact is a "mixin" language, so there is no concept of this "extending" `fungible-v2`. Instead,
implementing fungibles will implement _both_ `fungible-v2` and `restricted-fungible-v1`.


## Backwards Compatibility

This presents no conflicts with its intended interop with `fungible-v2` (or `fungible-v1`).

## Specification

- Interface: [restricted-fungible-v1.pact](pact/restricted-fungible-v1.pact)
- Reference implementation: [restricted-fungible-v1-reference.pact](pact/restricted-fungible-v1-reference.pact)
- Test: [restricted-fungible-v1.repl](pact/restricted-fungible-v1.repl)

## References
* ERC-1404: <https://github.com/ethereum/EIPs/issues/1404>
* fungible-v2: <https://github.com/kadena-io/KIPs/blob/master/kip-0005.md>
45 changes: 45 additions & 0 deletions kip-0009/pact/include/fungible-util.pact
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

(namespace (read-msg 'ns))

(module fungible-util GOVERNANCE

(defcap GOVERNANCE ()
(enforce-guard (keyset-ref-guard 'swap-ns-admin)))

(defun enforce-valid-amount
( precision:integer
amount:decimal
)
(enforce (> amount 0.0) "Positive non-zero amount")
(enforce-precision precision amount)
)

(defun enforce-valid-account (account:string)
(enforce (> (length account) 2) "minimum account length")
)

(defun enforce-precision
( precision:integer
amount:decimal
)
(enforce
(= (floor amount precision) amount)
"precision violation")
)

(defun enforce-valid-transfer
( sender:string
receiver:string
precision:integer
amount:decimal)
(enforce (!= sender receiver)
"sender cannot be the receiver of a transfer")
(enforce-valid-amount precision amount)
(enforce-valid-account sender)
(enforce-valid-account receiver)
)




)
135 changes: 135 additions & 0 deletions kip-0009/pact/include/fungible-v2.pact
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
(interface fungible-v2

" Standard for fungible coins and tokens as specified in KIP-0002. "

; ----------------------------------------------------------------------
; Schema

(defschema account-details
@doc "Schema for results of 'account' operation."
@model [ (invariant (!= "" sender)) ]

account:string
balance:decimal
guard:guard)


; ----------------------------------------------------------------------
; Caps

(defcap TRANSFER:bool
( sender:string
receiver:string
amount:decimal
)
@doc " Managed capability sealing AMOUNT for transfer from SENDER to \
\ RECEIVER. Permits any number of transfers up to AMOUNT."
@managed amount TRANSFER-mgr
)

(defun TRANSFER-mgr:decimal
( managed:decimal
requested:decimal
)
@doc " Manages TRANSFER AMOUNT linearly, \
\ such that a request for 1.0 amount on a 3.0 \
\ managed quantity emits updated amount 2.0."
)

; ----------------------------------------------------------------------
; Functionality


(defun transfer:string
( sender:string
receiver:string
amount:decimal
)
@doc " Transfer AMOUNT between accounts SENDER and RECEIVER. \
\ Fails if either SENDER or RECEIVER does not exist."
@model [ (property (> amount 0.0))
(property (!= sender ""))
(property (!= receiver ""))
(property (!= sender receiver))
]
)

(defun transfer-create:string
( sender:string
receiver:string
receiver-guard:guard
amount:decimal
)
@doc " Transfer AMOUNT between accounts SENDER and RECEIVER. \
\ Fails if SENDER does not exist. If RECEIVER exists, guard \
\ must match existing value. If RECEIVER does not exist, \
\ RECEIVER account is created using RECEIVER-GUARD. \
\ Subject to management by TRANSFER capability."
@model [ (property (> amount 0.0))
(property (!= sender ""))
(property (!= receiver ""))
(property (!= sender receiver))
]
)

(defpact transfer-crosschain:string
( sender:string
receiver:string
receiver-guard:guard
target-chain:string
amount:decimal
)
@doc " 2-step pact to transfer AMOUNT from SENDER on current chain \
\ to RECEIVER on TARGET-CHAIN via SPV proof. \
\ TARGET-CHAIN must be different than current chain id. \
\ First step debits AMOUNT coins in SENDER account and yields \
\ RECEIVER, RECEIVER_GUARD and AMOUNT to TARGET-CHAIN. \
\ Second step continuation is sent into TARGET-CHAIN with proof \
\ obtained from the spv 'output' endpoint of Chainweb. \
\ Proof is validated and RECEIVER is credited with AMOUNT \
\ creating account with RECEIVER_GUARD as necessary."
@model [ (property (> amount 0.0))
(property (!= sender ""))
(property (!= receiver ""))
(property (!= sender receiver))
(property (!= target-chain ""))
]
)

(defun get-balance:decimal
( account:string )
" Get balance for ACCOUNT. Fails if account does not exist."
)

(defun details:object{account-details}
( account: string )
" Get an object with details of ACCOUNT. \
\ Fails if account does not exist."
)

(defun precision:integer
()
"Return the maximum allowed decimal precision."
)

(defun enforce-unit:bool
( amount:decimal )
" Enforce minimum precision allowed for transactions."
)

(defun create-account:string
( account:string
guard:guard
)
" Create ACCOUNT with 0.0 balance, with GUARD controlling access."
)

(defun rotate:string
( account:string
new-guard:guard
)
" Rotate guard for ACCOUNT. Transaction is validated against \
\ existing guard before installing new guard. "
)

)
Loading