swap.gno

2.28 Kb ยท 98 lines
 1package atomicswap
 2
 3import (
 4	"crypto/sha256"
 5	"encoding/hex"
 6	"std"
 7	"time"
 8
 9	"gno.land/p/demo/ufmt"
10)
11
12// Swap represents an atomic swap contract.
13type Swap struct {
14	sender    std.Address
15	recipient std.Address
16	hashlock  string
17	timelock  time.Time
18	claimed   bool
19	refunded  bool
20	amountStr string
21	sendFn    func(to std.Address)
22}
23
24func newSwap(
25	sender std.Address,
26	recipient std.Address,
27	hashlock string,
28	timelock time.Time,
29	amountStr string,
30	sendFn func(std.Address),
31) *Swap {
32	require(time.Now().Before(timelock), "timelock must be in the future")
33	require(hashlock != "", "hashlock must not be empty")
34	return &Swap{
35		recipient: recipient,
36		sender:    sender,
37		hashlock:  hashlock,
38		timelock:  timelock,
39		claimed:   false,
40		refunded:  false,
41		sendFn:    sendFn,
42		amountStr: amountStr,
43	}
44}
45
46// Claim allows the recipient to claim the funds if they provide the correct preimage.
47func (s *Swap) Claim(preimage string) {
48	require(!s.claimed, "already claimed")
49	require(!s.refunded, "already refunded")
50	require(std.PreviousRealm().Address() == s.recipient, "unauthorized")
51
52	hashlock := sha256.Sum256([]byte(preimage))
53	hashlockHex := hex.EncodeToString(hashlock[:])
54	require(hashlockHex == s.hashlock, "invalid preimage")
55
56	s.claimed = true
57	s.sendFn(s.recipient)
58}
59
60// Refund allows the sender to refund the funds after the timelock has expired.
61func (s *Swap) Refund() {
62	require(!s.claimed, "already claimed")
63	require(!s.refunded, "already refunded")
64	require(std.PreviousRealm().Address() == s.sender, "unauthorized")
65	require(time.Now().After(s.timelock), "timelock not expired")
66
67	s.refunded = true
68	s.sendFn(s.sender)
69}
70
71func (s Swap) Status() string {
72	switch {
73	case s.refunded:
74		return "refunded"
75	case s.claimed:
76		return "claimed"
77	case s.TimeRemaining() < 0:
78		return "expired"
79	default:
80		return "active"
81	}
82}
83
84func (s Swap) TimeRemaining() time.Duration {
85	remaining := time.Until(s.timelock)
86	if remaining < 0 {
87		return 0
88	}
89	return remaining
90}
91
92// String returns the current state of the swap.
93func (s Swap) String() string {
94	return ufmt.Sprintf(
95		"- status: %s\n- sender: %s\n- recipient: %s\n- amount: %s\n- hashlock: %s\n- timelock: %s\n- remaining: %s",
96		s.Status(), s.sender, s.recipient, s.amountStr, s.hashlock, s.timelock.Format(time.RFC3339), s.TimeRemaining().String(),
97	)
98}