wrapped.gno
3.83 Kb · 124 lines
1// Package wrapped is an accounting-only port of Wrapped Ether (WETH).
2//
3// The canonical WETH contract lets anyone lock native ETH and mint a 1:1
4// ERC-20 representation (and burn it back). On gno.land / topaz there is no
5// msg.value / payable, so there is NO real coin transfer here: Deposit and
6// Withdraw are pure accounting operations that model the wrap/unwrap. Balances
7// and totalSupply are plain uint64 ledgers kept in an avl.Tree.
8package wrapped
9
10import (
11 "strconv"
12
13 "chain"
14 "chain/runtime/unsafe"
15
16 "gno.land/p/nt/avl/v0"
17)
18
19// balances maps owner address (string) -> wrapped balance (uint64).
20var (
21 balances avl.Tree
22 totalSupply uint64
23)
24
25// Deposit wraps `amount` units of the (notional) native coin, crediting the
26// caller's wrapped balance and the global totalSupply. Accounting only: no real
27// coin ever moves — it models locking native funds to mint the wrapped token.
28func Deposit(cur realm, amount uint64) {
29 if amount == 0 {
30 panic("wrapped: deposit amount must be > 0")
31 }
32 owner := unsafe.PreviousRealm().Address()
33 setBalance(owner, balanceOf(owner)+amount)
34 totalSupply += amount
35 chain.Emit("Deposit", "dst", owner.String(), "wad", strconv.FormatUint(amount, 10))
36}
37
38// Withdraw unwraps `amount` units, burning them from the caller's balance and
39// from totalSupply. Panics if the caller lacks the balance.
40func Withdraw(cur realm, amount uint64) {
41 if amount == 0 {
42 panic("wrapped: withdraw amount must be > 0")
43 }
44 owner := unsafe.PreviousRealm().Address()
45 bal := balanceOf(owner)
46 if bal < amount {
47 panic("wrapped: insufficient balance")
48 }
49 setBalance(owner, bal-amount)
50 totalSupply -= amount
51 chain.Emit("Withdrawal", "src", owner.String(), "wad", strconv.FormatUint(amount, 10))
52}
53
54// Transfer moves `amount` wrapped units from the caller to `to`.
55func Transfer(cur realm, to address, amount uint64) {
56 if amount == 0 {
57 panic("wrapped: transfer amount must be > 0")
58 }
59 from := unsafe.PreviousRealm().Address()
60 if from == to {
61 panic("wrapped: cannot transfer to self")
62 }
63 fromBal := balanceOf(from)
64 if fromBal < amount {
65 panic("wrapped: insufficient balance")
66 }
67 setBalance(from, fromBal-amount)
68 setBalance(to, balanceOf(to)+amount)
69 chain.Emit("Transfer", "src", from.String(), "dst", to.String(), "wad", strconv.FormatUint(amount, 10))
70}
71
72// balanceOf returns the wrapped balance of `owner` (read-only, not crossing).
73func balanceOf(owner address) uint64 {
74 v := balances.Get(owner.String())
75 if v == nil {
76 return 0
77 }
78 return v.(uint64)
79}
80
81// BalanceOf is the exported read-only accessor for a single owner.
82func BalanceOf(owner address) uint64 { return balanceOf(owner) }
83
84// TotalSupply returns the total amount of wrapped units in existence.
85func TotalSupply() uint64 { return totalSupply }
86
87func setBalance(owner address, amount uint64) {
88 if amount == 0 {
89 balances.Remove(owner.String())
90 return
91 }
92 balances.Set(owner.String(), amount)
93}
94
95// ---- pure/read-only helpers (unit-tested) ----
96
97// canDebit reports whether `bal` can cover `amount` for a burn/transfer.
98func canDebit(bal, amount uint64) bool {
99 return amount > 0 && bal >= amount
100}
101
102// formatUnits renders a raw uint64 amount as a decimal string.
103func formatUnits(n uint64) string {
104 return strconv.FormatUint(n, 10)
105}
106
107// Render shows the total supply and a table of non-zero balances.
108func Render(path string) string {
109 out := "# Wrapped (WETH-style, accounting-only)\n\n"
110 out += "Deposit mints wrapped units, Withdraw burns them. No real coins move — this is a pure ledger.\n\n"
111 out += "**Total supply:** " + formatUnits(totalSupply) + "\n\n"
112
113 if balances.Size() == 0 {
114 out += "_No balances yet. Call `Deposit` to wrap some units._\n"
115 return out
116 }
117
118 out += "| Owner | Balance |\n|---|---:|\n"
119 balances.Iterate("", "", func(key string, value interface{}) bool {
120 out += "| `" + key + "` | " + formatUnits(value.(uint64)) + " |\n"
121 return false
122 })
123 return out
124}