Skip to content

Commit 942d2b5

Browse files
committed
SBT bonce logic update
1 parent 06900d6 commit 942d2b5

File tree

6 files changed

+110
-145
lines changed

6 files changed

+110
-145
lines changed

packages/contracts/sbt-item/SbtItem.data.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const OperationCodes = {
7373
OwnershipProofBounced: 0xc18e86d2,
7474
RequestOwnerInfo: 0xd0c3bfea,
7575
OwnerInfo: 0x0dd607e3,
76-
OwnerInfoBounced: 0x7ca7b0fe,
76+
TakeExcess: 0xd136d3b3,
7777
Destroy: 0x1f04537a,
7878
Revoke: 0x6f89f5e3
7979
}
@@ -103,6 +103,12 @@ export const Queries = {
103103
msgBody.bits.writeUint(params.queryId || 0, 64)
104104
return msgBody
105105
},
106+
takeExcess: (params: { queryId?: number}) => {
107+
let msgBody = new Cell()
108+
msgBody.bits.writeUint(OperationCodes.TakeExcess, 32)
109+
msgBody.bits.writeUint(params.queryId || 0, 64)
110+
return msgBody
111+
},
106112
proveOwnership: (params: { queryId?: number, to: Address, data: Cell, withContent:boolean }) => {
107113
let msgBody = new Cell()
108114
msgBody.bits.writeUint(OperationCodes.ProveOwnership, 32)

packages/contracts/sbt-item/SbtItem.spec.ts

+79-114
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Cell, CellMessage, CommonMessageInfo, ExternalMessage, InternalMessage, toNano} from "ton";
1+
import {Cell, CellMessage, CommonMessageInfo, ExternalMessage, fromNano, InternalMessage, toNano} from "ton";
22
import {randomAddress} from "../../utils/randomAddress";
33
import {SbtItemData, SbtSingleData, OperationCodes, Queries} from "./SbtItem.data";
44
import {SbtItemLocal} from "./SbtItemLocal";
@@ -399,66 +399,9 @@ describe('sbt item smc', () => {
399399

400400
let op = response.readUintNumber(32)
401401
let queryId = response.readUintNumber(64)
402-
let index = response.readUintNumber(256)
403-
let owner = response.readAddress() as Address
404-
let data = response.readRef()
405-
let revokedAt = response.readUintNumber(64)
406-
response.readBit()
407-
408402

409403
expect(op).toEqual(OperationCodes.OwnershipProofBounced)
410404
expect(queryId).toEqual(0)
411-
expect(index).toEqual(777)
412-
expect(owner.toFriendly()).toEqual(defaultConfig.ownerAddress.toFriendly())
413-
expect(data.readUint(16).toNumber()).toEqual(888)
414-
expect(revokedAt).toEqual(0)
415-
})
416-
417-
it('should prove proof bounce to initiator', async () => {
418-
let sbt = await SbtItemLocal.createFromConfig(defaultConfig)
419-
420-
let dataCell = new Cell()
421-
dataCell.bits.writeUint(888,16)
422-
423-
let initer = randomAddress()
424-
425-
let res = await sbt.contract.sendInternalMessage(new InternalMessage({
426-
bounced: true,
427-
to: sbt.address,
428-
from: randomAddress(),
429-
value: toNano(1),
430-
bounce: false,
431-
body: new CommonMessageInfo({
432-
body: new CellMessage(Queries.ownerInfo({
433-
id: 777,
434-
initiator: initer,
435-
owner: defaultConfig.ownerAddress,
436-
data: dataCell,
437-
}, true))
438-
})
439-
}))
440-
441-
expect(res.exit_code).toEqual(0)
442-
443-
let [responseMessage] = res.actionList as [SendMsgAction]
444-
let response = responseMessage.message.body.beginParse()
445-
446-
let op = response.readUintNumber(32)
447-
let queryId = response.readUintNumber(64)
448-
let index = response.readUintNumber(256)
449-
let initiator = response.readAddress() as Address
450-
let owner = response.readAddress() as Address
451-
let data = response.readRef()
452-
let revokedAt = response.readUintNumber(64)
453-
response.readBit()
454-
455-
expect(op).toEqual(OperationCodes.OwnerInfoBounced)
456-
expect(queryId).toEqual(0)
457-
expect(index).toEqual(777)
458-
expect(initiator.toFriendly()).toEqual(initer.toFriendly())
459-
expect(owner.toFriendly()).toEqual(defaultConfig.ownerAddress.toFriendly())
460-
expect(data.readUint(16).toNumber()).toEqual(888)
461-
expect(revokedAt).toEqual(0)
462405
})
463406

464407
it('should not verify ownership non bounced', async () => {
@@ -519,6 +462,45 @@ describe('sbt item smc', () => {
519462

520463
expect(res.exit_code).toEqual(401)
521464
})
465+
466+
it('should not take excess', async () => {
467+
let sbt = await SbtItemLocal.createFromConfig(defaultConfig)
468+
let res = await sbt.contract.sendInternalMessage(new InternalMessage({
469+
to: sbt.address,
470+
from: defaultConfig.authorityAddress,
471+
value: toNano(1),
472+
bounce: false,
473+
body: new CommonMessageInfo({
474+
body: new CellMessage(Queries.takeExcess({}))
475+
})
476+
}))
477+
478+
expect(res.exit_code).toEqual(401)
479+
})
480+
481+
it('should take excess', async () => {
482+
let sbt = await SbtItemLocal.createFromConfig(defaultConfig)
483+
let res = await sbt.contract.sendInternalMessage(new InternalMessage({
484+
to: sbt.address,
485+
from: defaultConfig.ownerAddress,
486+
value: toNano(1),
487+
bounce: false,
488+
body: new CommonMessageInfo({
489+
body: new CellMessage(Queries.takeExcess({}))
490+
})
491+
}))
492+
493+
expect(res.exit_code).toEqual(0)
494+
495+
let [reserve, responseMessage] = res.actionList as [ReserveCurrencyAction,SendMsgAction]
496+
expect(reserve.mode).toEqual(0)
497+
expect(fromNano(reserve.currency.coins)).toEqual("0.05")
498+
499+
let response = responseMessage.message.body.beginParse()
500+
let op = response.readUintNumber(32)
501+
expect(op).toEqual(OperationCodes.excesses)
502+
expect(responseMessage.mode).toEqual(128)
503+
})
522504
})
523505

524506
describe('single sbt', () => {
@@ -858,65 +840,9 @@ describe('single sbt', () => {
858840

859841
let op = response.readUintNumber(32)
860842
let queryId = response.readUintNumber(64)
861-
let index = response.readUintNumber(256)
862-
let owner = response.readAddress() as Address
863-
let data = response.readRef()
864-
let revokedAt = response.readUintNumber(64)
865-
response.readBit()
866843

867844
expect(op).toEqual(OperationCodes.OwnershipProofBounced)
868845
expect(queryId).toEqual(0)
869-
expect(index).toEqual(777)
870-
expect(owner.toFriendly()).toEqual(defaultConfig.ownerAddress.toFriendly())
871-
expect(data.readUint(16).toNumber()).toEqual(888)
872-
expect(revokedAt).toEqual(0)
873-
})
874-
875-
it('should prove proof bounce to initiator', async () => {
876-
let sbt = await SbtItemLocal.createSingle(singleConfig)
877-
878-
let dataCell = new Cell()
879-
dataCell.bits.writeUint(888,16)
880-
881-
let initer = randomAddress()
882-
883-
let res = await sbt.contract.sendInternalMessage(new InternalMessage({
884-
bounced: true,
885-
to: sbt.address,
886-
from: randomAddress(),
887-
value: toNano(1),
888-
bounce: false,
889-
body: new CommonMessageInfo({
890-
body: new CellMessage(Queries.ownerInfo({
891-
id: 777,
892-
initiator: initer,
893-
owner: singleConfig.ownerAddress,
894-
data: dataCell,
895-
}, true))
896-
})
897-
}))
898-
899-
expect(res.exit_code).toEqual(0)
900-
901-
let [responseMessage] = res.actionList as [SendMsgAction]
902-
let response = responseMessage.message.body.beginParse()
903-
904-
let op = response.readUintNumber(32)
905-
let queryId = response.readUintNumber(64)
906-
let index = response.readUintNumber(256)
907-
let initiator = response.readAddress() as Address
908-
let owner = response.readAddress() as Address
909-
let data = response.readRef()
910-
let revokedAt = response.readUintNumber(64)
911-
response.readBit()
912-
913-
expect(op).toEqual(OperationCodes.OwnerInfoBounced)
914-
expect(queryId).toEqual(0)
915-
expect(index).toEqual(777)
916-
expect(initiator.toFriendly()).toEqual(initer.toFriendly())
917-
expect(owner.toFriendly()).toEqual(defaultConfig.ownerAddress.toFriendly())
918-
expect(data.readUint(16).toNumber()).toEqual(888)
919-
expect(revokedAt).toEqual(0)
920846
})
921847

922848
it('should not pass prove ownership non bounced', async () => {
@@ -1006,4 +932,43 @@ describe('single sbt', () => {
1006932

1007933
expect(res.exit_code).toEqual(401)
1008934
})
935+
936+
it('should not take excess', async () => {
937+
let sbt = await SbtItemLocal.createSingle(singleConfig)
938+
let res = await sbt.contract.sendInternalMessage(new InternalMessage({
939+
to: sbt.address,
940+
from: defaultConfig.authorityAddress,
941+
value: toNano(1),
942+
bounce: false,
943+
body: new CommonMessageInfo({
944+
body: new CellMessage(Queries.takeExcess({}))
945+
})
946+
}))
947+
948+
expect(res.exit_code).toEqual(401)
949+
})
950+
951+
it('should take excess', async () => {
952+
let sbt = await SbtItemLocal.createSingle(singleConfig)
953+
let res = await sbt.contract.sendInternalMessage(new InternalMessage({
954+
to: sbt.address,
955+
from: defaultConfig.ownerAddress,
956+
value: toNano(1),
957+
bounce: false,
958+
body: new CommonMessageInfo({
959+
body: new CellMessage(Queries.takeExcess({}))
960+
})
961+
}))
962+
963+
expect(res.exit_code).toEqual(0)
964+
965+
let [reserve, responseMessage] = res.actionList as [ReserveCurrencyAction,SendMsgAction]
966+
expect(reserve.mode).toEqual(0)
967+
expect(fromNano(reserve.currency.coins)).toEqual("0.05")
968+
969+
let response = responseMessage.message.body.beginParse()
970+
let op = response.readUintNumber(32)
971+
expect(op).toEqual(OperationCodes.excesses)
972+
expect(responseMessage.mode).toEqual(128)
973+
})
1009974
})

packages/contracts/sources/op-codes.fc

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ int op::editorship_assigned() asm "0x511a4463 PUSHINT";
1414
;; SBT
1515
int op::request_owner() asm "0xd0c3bfea PUSHINT";
1616
int op::owner_info() asm "0x0dd607e3 PUSHINT";
17-
int op::owner_info_bounced() asm "0x7ca7b0fe PUSHINT";
1817

1918
int op::prove_ownership() asm "0x04ded148 PUSHINT";
2019
int op::ownership_proof() asm "0x0524c7ae PUSHINT";
2120
int op::ownership_proof_bounced() asm "0xc18e86d2 PUSHINT";
2221

2322
int op::destroy() asm "0x1f04537a PUSHINT";
2423
int op::revoke() asm "0x6f89f5e3 PUSHINT";
24+
int op::take_excess() asm "0xd136d3b3 PUSHINT";

packages/contracts/sources/sbt-item.fc

+11-14
Original file line numberDiff line numberDiff line change
@@ -98,23 +98,11 @@ global int storage::revoked_at;
9898
if (flags & 1) { ;; route all prove_ownership bounced messages to owner
9999
;; first op was 0xffffffff, because of bounced, now we need to read real one
100100
op = in_msg_body~load_uint(32);
101-
int query_id = in_msg_body~load_uint(64);
102101

103102
if (op == op::ownership_proof()) {
104-
var msg = begin_cell().store_slice(in_msg_body);
105-
106-
;; mode 64 = carry all the remaining value of the inbound message
107-
send_msg(flag::regular(), storage::owner_address, 0, op::ownership_proof_bounced(), query_id, msg, 64);
108-
}
109-
110-
if (op == op::owner_info()) {
111-
int id = in_msg_body~load_uint(256);
112-
slice initiator = in_msg_body~load_msg_addr();
113-
114-
var msg = begin_cell().store_uint(id, 256).store_slice(initiator).store_slice(in_msg_body);
115-
103+
int query_id = in_msg_body~load_uint(64);
116104
;; mode 64 = carry all the remaining value of the inbound message
117-
send_msg(flag::regular(), initiator, 0, op::owner_info_bounced(), query_id, msg, 64);
105+
send_msg(flag::regular(), storage::owner_address, 0, op::ownership_proof_bounced(), query_id, null(), 64);
118106
}
119107
return ();
120108
}
@@ -189,6 +177,15 @@ global int storage::revoked_at;
189177
store_data();
190178
return ();
191179
}
180+
if (op == op::take_excess()) {
181+
throw_unless(401, equal_slices(storage::owner_address, sender_address));
182+
183+
;; reserve amount for storage
184+
raw_reserve(min_tons_for_storage(), 0);
185+
186+
send_msg(flag::regular(), sender_address, 0, op::excesses(), query_id, null(), 128);
187+
return ();
188+
}
192189
if (op == op::transfer()) {
193190
throw(413);
194191
}

packages/contracts/sources/sbt-single.fc

+11-14
Original file line numberDiff line numberDiff line change
@@ -118,23 +118,11 @@ global int storage::revoked_at;
118118
if (flags & 1) { ;; route all prove_ownership bounced messages to owner
119119
;; first op was 0xffffffff, because of bounced, now we need to read real one
120120
op = in_msg_body~load_uint(32);
121-
int query_id = in_msg_body~load_uint(64);
122121

123122
if (op == op::ownership_proof()) {
124-
var msg = begin_cell().store_slice(in_msg_body);
125-
126-
;; mode 64 = carry all the remaining value of the inbound message
127-
send_msg(flag::regular(), storage::owner_address, 0, op::ownership_proof_bounced(), query_id, msg, 64);
128-
}
129-
130-
if (op == op::owner_info()) {
131-
int id = in_msg_body~load_uint(256);
132-
slice initiator = in_msg_body~load_msg_addr();
133-
134-
var msg = begin_cell().store_uint(id, 256).store_slice(initiator).store_slice(in_msg_body);
135-
123+
int query_id = in_msg_body~load_uint(64);
136124
;; mode 64 = carry all the remaining value of the inbound message
137-
send_msg(flag::regular(), initiator, 0, op::owner_info_bounced(), query_id, msg, 64);
125+
send_msg(flag::regular(), storage::owner_address, 0, op::ownership_proof_bounced(), query_id, null(), 64);
138126
}
139127
return ();
140128
}
@@ -219,6 +207,15 @@ global int storage::revoked_at;
219207
store_data();
220208
return ();
221209
}
210+
if (op == op::take_excess()) {
211+
throw_unless(401, equal_slices(storage::owner_address, sender_address));
212+
213+
;; reserve amount for storage
214+
raw_reserve(min_tons_for_storage(), 0);
215+
216+
send_msg(flag::regular(), sender_address, 0, op::excesses(), query_id, null(), 128);
217+
return ();
218+
}
222219
if (op == op::transfer()) {
223220
throw(413);
224221
}

sbt.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ After that SBT will send transfer to `dest` with scheme:
5858
owner_info#0dd607e3 query_id:uint64 item_id:uint256 initiator:MsgAddress owner:MsgAddress
5959
data:^Cell revoked_at:uint64 content:(Maybe ^Cell)
6060
```
61-
If something goes wrong and target contract not accepts message, and it will be bounced back to SBT, SBT will proxy this bounce to initiator, this way coins will not stuck on SBT.
61+
If something goes wrong and target contract not accepts message, and it will be bounced back to SBT, amount will stay on SBT.
6262

6363
#### Verify SBT contract example
6464

0 commit comments

Comments
 (0)