hive-stream 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCUMENTATION.md +50 -2
- package/README.md +44 -3
- package/dist/adapters/base.adapter.d.ts +5 -0
- package/dist/adapters/base.adapter.js +9 -0
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +6 -6
- package/dist/adapters/mongodb.adapter.js +36 -21
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +7 -0
- package/dist/adapters/postgresql.adapter.js +46 -19
- package/dist/adapters/postgresql.adapter.js.map +1 -1
- package/dist/adapters/sqlite.adapter.d.ts +4 -0
- package/dist/adapters/sqlite.adapter.js +10 -0
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.d.ts +13 -3
- package/dist/api.js +96 -62
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +7 -1
- package/dist/config.js.map +1 -1
- package/dist/contracts/auctionhouse.contract.d.ts +4 -0
- package/dist/contracts/auctionhouse.contract.js +234 -0
- package/dist/contracts/auctionhouse.contract.js.map +1 -0
- package/dist/contracts/booking.contract.d.ts +4 -0
- package/dist/contracts/booking.contract.js +225 -0
- package/dist/contracts/booking.contract.js.map +1 -0
- package/dist/contracts/bountyboard.contract.d.ts +4 -0
- package/dist/contracts/bountyboard.contract.js +233 -0
- package/dist/contracts/bountyboard.contract.js.map +1 -0
- package/dist/contracts/bundlemarketplace.contract.d.ts +4 -0
- package/dist/contracts/bundlemarketplace.contract.js +195 -0
- package/dist/contracts/bundlemarketplace.contract.js.map +1 -0
- package/dist/contracts/charitymatch.contract.d.ts +4 -0
- package/dist/contracts/charitymatch.contract.js +172 -0
- package/dist/contracts/charitymatch.contract.js.map +1 -0
- package/dist/contracts/coinflip.contract.js +7 -1
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/crowdfund.contract.d.ts +4 -0
- package/dist/contracts/crowdfund.contract.js +290 -0
- package/dist/contracts/crowdfund.contract.js.map +1 -0
- package/dist/contracts/dcabot.contract.d.ts +4 -0
- package/dist/contracts/dcabot.contract.js +217 -0
- package/dist/contracts/dcabot.contract.js.map +1 -0
- package/dist/contracts/dice.contract.js +7 -1
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/domainregistry.contract.d.ts +4 -0
- package/dist/contracts/domainregistry.contract.js +232 -0
- package/dist/contracts/domainregistry.contract.js.map +1 -0
- package/dist/contracts/exchange.contract.js +209 -168
- package/dist/contracts/exchange.contract.js.map +1 -1
- package/dist/contracts/fanclub.contract.d.ts +4 -0
- package/dist/contracts/fanclub.contract.js +193 -0
- package/dist/contracts/fanclub.contract.js.map +1 -0
- package/dist/contracts/giftcard.contract.d.ts +4 -0
- package/dist/contracts/giftcard.contract.js +158 -0
- package/dist/contracts/giftcard.contract.js.map +1 -0
- package/dist/contracts/grantrounds.contract.d.ts +4 -0
- package/dist/contracts/grantrounds.contract.js +265 -0
- package/dist/contracts/grantrounds.contract.js.map +1 -0
- package/dist/contracts/groupbuy.contract.d.ts +4 -0
- package/dist/contracts/groupbuy.contract.js +198 -0
- package/dist/contracts/groupbuy.contract.js.map +1 -0
- package/dist/contracts/helpers.d.ts +64 -0
- package/dist/contracts/helpers.js +159 -0
- package/dist/contracts/helpers.js.map +1 -0
- package/dist/contracts/insurancepool.contract.d.ts +4 -0
- package/dist/contracts/insurancepool.contract.js +281 -0
- package/dist/contracts/insurancepool.contract.js.map +1 -0
- package/dist/contracts/invoice.contract.d.ts +4 -0
- package/dist/contracts/invoice.contract.js +193 -0
- package/dist/contracts/invoice.contract.js.map +1 -0
- package/dist/contracts/launchpad.contract.d.ts +4 -0
- package/dist/contracts/launchpad.contract.js +225 -0
- package/dist/contracts/launchpad.contract.js.map +1 -0
- package/dist/contracts/lotto.contract.js +53 -37
- package/dist/contracts/lotto.contract.js.map +1 -1
- package/dist/contracts/multisigtreasury.contract.d.ts +4 -0
- package/dist/contracts/multisigtreasury.contract.js +245 -0
- package/dist/contracts/multisigtreasury.contract.js.map +1 -0
- package/dist/contracts/nft.contract.d.ts +1 -0
- package/dist/contracts/nft.contract.js +236 -192
- package/dist/contracts/nft.contract.js.map +1 -1
- package/dist/contracts/oraclebounty.contract.d.ts +4 -0
- package/dist/contracts/oraclebounty.contract.js +250 -0
- package/dist/contracts/oraclebounty.contract.js.map +1 -0
- package/dist/contracts/payroll.contract.d.ts +4 -0
- package/dist/contracts/payroll.contract.js +232 -0
- package/dist/contracts/payroll.contract.js.map +1 -0
- package/dist/contracts/paywall.contract.d.ts +4 -0
- package/dist/contracts/paywall.contract.js +185 -0
- package/dist/contracts/paywall.contract.js.map +1 -0
- package/dist/contracts/poll.contract.js +2 -0
- package/dist/contracts/poll.contract.js.map +1 -1
- package/dist/contracts/predictionmarket.contract.d.ts +4 -0
- package/dist/contracts/predictionmarket.contract.js +213 -0
- package/dist/contracts/predictionmarket.contract.js.map +1 -0
- package/dist/contracts/proposaltimelock.contract.d.ts +4 -0
- package/dist/contracts/proposaltimelock.contract.js +250 -0
- package/dist/contracts/proposaltimelock.contract.js.map +1 -0
- package/dist/contracts/questpass.contract.d.ts +4 -0
- package/dist/contracts/questpass.contract.js +214 -0
- package/dist/contracts/questpass.contract.js.map +1 -0
- package/dist/contracts/referral.contract.d.ts +4 -0
- package/dist/contracts/referral.contract.js +238 -0
- package/dist/contracts/referral.contract.js.map +1 -0
- package/dist/contracts/rental.contract.d.ts +4 -0
- package/dist/contracts/rental.contract.js +221 -0
- package/dist/contracts/rental.contract.js.map +1 -0
- package/dist/contracts/revenuesplit.contract.d.ts +4 -0
- package/dist/contracts/revenuesplit.contract.js +211 -0
- package/dist/contracts/revenuesplit.contract.js.map +1 -0
- package/dist/contracts/rps.contract.js +17 -1
- package/dist/contracts/rps.contract.js.map +1 -1
- package/dist/contracts/savings.contract.d.ts +4 -0
- package/dist/contracts/savings.contract.js +208 -0
- package/dist/contracts/savings.contract.js.map +1 -0
- package/dist/contracts/subscription.contract.d.ts +4 -0
- package/dist/contracts/subscription.contract.js +241 -0
- package/dist/contracts/subscription.contract.js.map +1 -0
- package/dist/contracts/sweepstakes.contract.d.ts +4 -0
- package/dist/contracts/sweepstakes.contract.js +209 -0
- package/dist/contracts/sweepstakes.contract.js.map +1 -0
- package/dist/contracts/ticketing.contract.d.ts +4 -0
- package/dist/contracts/ticketing.contract.js +185 -0
- package/dist/contracts/ticketing.contract.js.map +1 -0
- package/dist/contracts/tipjar.contract.js +2 -0
- package/dist/contracts/tipjar.contract.js.map +1 -1
- package/dist/contracts/token.contract.js +135 -125
- package/dist/contracts/token.contract.js.map +1 -1
- package/dist/index.d.ts +39 -0
- package/dist/index.js +71 -1
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +7 -0
- package/dist/metadata.js +64 -1
- package/dist/metadata.js.map +1 -1
- package/dist/providers/block-provider.d.ts +22 -0
- package/dist/providers/block-provider.js +3 -0
- package/dist/providers/block-provider.js.map +1 -0
- package/dist/providers/haf-client.d.ts +30 -0
- package/dist/providers/haf-client.js +119 -0
- package/dist/providers/haf-client.js.map +1 -0
- package/dist/providers/haf-provider.d.ts +49 -0
- package/dist/providers/haf-provider.js +256 -0
- package/dist/providers/haf-provider.js.map +1 -0
- package/dist/providers/hive-provider.d.ts +13 -0
- package/dist/providers/hive-provider.js +25 -0
- package/dist/providers/hive-provider.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.js +21 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/streamer.d.ts +21 -1
- package/dist/streamer.js +187 -62
- package/dist/streamer.js.map +1 -1
- package/dist/utils.js +11 -2
- package/dist/utils.js.map +1 -1
- package/package.json +16 -1
- package/.claude/settings.local.json +0 -12
- package/.env.example +0 -3
- package/.travis.yml +0 -11
- package/AGENTS.md +0 -35
- package/CLAUDE.md +0 -75
- package/ecosystem.config.js +0 -17
- package/examples/contracts/README.md +0 -8
- package/examples/contracts/exchange.ts +0 -38
- package/examples/contracts/poll.ts +0 -21
- package/examples/contracts/rps.ts +0 -19
- package/examples/contracts/tipjar.ts +0 -19
- package/jest.config.js +0 -9
- package/test-contract-block.md +0 -19
- package/tests/actions.spec.ts +0 -252
- package/tests/adapters/actions-persistence.spec.ts +0 -144
- package/tests/adapters/postgresql.adapter.spec.ts +0 -127
- package/tests/adapters/sqlite.adapter.spec.ts +0 -181
- package/tests/config-input.spec.ts +0 -90
- package/tests/contracts/coinflip.contract.spec.ts +0 -94
- package/tests/contracts/dice.contract.spec.ts +0 -87
- package/tests/contracts/entrants.json +0 -729
- package/tests/contracts/exchange.contract.spec.ts +0 -84
- package/tests/contracts/lotto.contract.spec.ts +0 -59
- package/tests/contracts/nft.contract.spec.ts +0 -948
- package/tests/contracts/token.contract.spec.ts +0 -90
- package/tests/exchanges/coingecko.exchange.spec.ts +0 -169
- package/tests/exchanges/exchange.base.spec.ts +0 -246
- package/tests/helpers/mock-adapter.ts +0 -214
- package/tests/helpers/mock-fetch.ts +0 -165
- package/tests/hive-chain-features.spec.ts +0 -319
- package/tests/hive-rates.spec.ts +0 -443
- package/tests/integration/hive-rates.integration.spec.ts +0 -35
- package/tests/metadata.spec.ts +0 -63
- package/tests/setup.ts +0 -30
- package/tests/streamer-actions.spec.ts +0 -274
- package/tests/streamer.spec.ts +0 -342
- package/tests/types/rates.spec.ts +0 -216
- package/tests/utils.spec.ts +0 -113
- package/tsconfig.build.json +0 -4
- package/tslint.json +0 -21
- package/wallaby.js +0 -26
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFanClubContract = createFanClubContract;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const contract_1 = require("./contract");
|
|
6
|
+
const helpers_1 = require("./helpers");
|
|
7
|
+
const DEFAULT_NAME = 'fanclub';
|
|
8
|
+
function createFanClubContract(options = {}) {
|
|
9
|
+
const name = options.name || DEFAULT_NAME;
|
|
10
|
+
const state = (0, helpers_1.createContractState)();
|
|
11
|
+
const perkSchema = zod_1.z.object({
|
|
12
|
+
perkId: helpers_1.identifierSchema,
|
|
13
|
+
minPoints: zod_1.z.number().int().min(0),
|
|
14
|
+
title: zod_1.z.string().min(1).max(80)
|
|
15
|
+
});
|
|
16
|
+
const createClubSchema = zod_1.z.object({
|
|
17
|
+
clubId: helpers_1.identifierSchema,
|
|
18
|
+
title: zod_1.z.string().min(3).max(140),
|
|
19
|
+
joinPrice: helpers_1.amountSchema,
|
|
20
|
+
asset: helpers_1.assetSchema,
|
|
21
|
+
perks: zod_1.z.array(perkSchema).max(25).optional(),
|
|
22
|
+
metadata: zod_1.z.record(zod_1.z.any()).optional()
|
|
23
|
+
});
|
|
24
|
+
const clubIdSchema = zod_1.z.object({
|
|
25
|
+
clubId: helpers_1.identifierSchema
|
|
26
|
+
});
|
|
27
|
+
const progressSchema = zod_1.z.object({
|
|
28
|
+
clubId: helpers_1.identifierSchema,
|
|
29
|
+
account: zod_1.z.string().min(3).max(32),
|
|
30
|
+
points: zod_1.z.number().int().min(1).max(1000000),
|
|
31
|
+
note: zod_1.z.string().max(280).optional()
|
|
32
|
+
});
|
|
33
|
+
const redeemSchema = zod_1.z.object({
|
|
34
|
+
clubId: helpers_1.identifierSchema,
|
|
35
|
+
perkId: helpers_1.identifierSchema
|
|
36
|
+
});
|
|
37
|
+
const initialize = async () => {
|
|
38
|
+
await (0, helpers_1.initializeTables)(state.adapter, [
|
|
39
|
+
`
|
|
40
|
+
CREATE TABLE IF NOT EXISTS fan_clubs (
|
|
41
|
+
club_id TEXT PRIMARY KEY,
|
|
42
|
+
owner TEXT NOT NULL,
|
|
43
|
+
title TEXT NOT NULL,
|
|
44
|
+
join_price TEXT NOT NULL,
|
|
45
|
+
asset TEXT NOT NULL,
|
|
46
|
+
perks_json TEXT NOT NULL,
|
|
47
|
+
metadata TEXT,
|
|
48
|
+
created_at DATETIME NOT NULL
|
|
49
|
+
)
|
|
50
|
+
`,
|
|
51
|
+
`
|
|
52
|
+
CREATE TABLE IF NOT EXISTS fan_club_members (
|
|
53
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
54
|
+
club_id TEXT NOT NULL,
|
|
55
|
+
account TEXT NOT NULL,
|
|
56
|
+
status TEXT NOT NULL,
|
|
57
|
+
renewals INTEGER NOT NULL,
|
|
58
|
+
points INTEGER NOT NULL,
|
|
59
|
+
created_at DATETIME NOT NULL,
|
|
60
|
+
updated_at DATETIME NOT NULL,
|
|
61
|
+
UNIQUE(club_id, account)
|
|
62
|
+
)
|
|
63
|
+
`,
|
|
64
|
+
`
|
|
65
|
+
CREATE TABLE IF NOT EXISTS fan_club_redemptions (
|
|
66
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
+
club_id TEXT NOT NULL,
|
|
68
|
+
account TEXT NOT NULL,
|
|
69
|
+
perk_id TEXT NOT NULL,
|
|
70
|
+
created_at DATETIME NOT NULL,
|
|
71
|
+
UNIQUE(club_id, account, perk_id)
|
|
72
|
+
)
|
|
73
|
+
`
|
|
74
|
+
]);
|
|
75
|
+
};
|
|
76
|
+
const createClub = async (payload, ctx) => {
|
|
77
|
+
const owner = (0, helpers_1.requireSender)(ctx);
|
|
78
|
+
const existing = await state.adapter.query('SELECT club_id FROM fan_clubs WHERE club_id = ?', [payload.clubId]);
|
|
79
|
+
if (existing.length > 0) {
|
|
80
|
+
throw new Error(`Club ${payload.clubId} already exists`);
|
|
81
|
+
}
|
|
82
|
+
await state.adapter.query(`INSERT INTO fan_clubs (
|
|
83
|
+
club_id, owner, title, join_price, asset, perks_json, metadata, created_at
|
|
84
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [payload.clubId, owner, payload.title, payload.joinPrice, payload.asset, JSON.stringify(payload.perks || []), JSON.stringify(payload.metadata || {}), new Date()]);
|
|
85
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'createClub', payload, {
|
|
86
|
+
action: 'fan_club_created',
|
|
87
|
+
data: {
|
|
88
|
+
clubId: payload.clubId,
|
|
89
|
+
owner
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const joinClub = async (payload, ctx) => {
|
|
94
|
+
const account = (0, helpers_1.requireSender)(ctx);
|
|
95
|
+
const payment = (0, helpers_1.getIncomingPayment)(ctx);
|
|
96
|
+
const rows = await state.adapter.query('SELECT * FROM fan_clubs WHERE club_id = ?', [payload.clubId]);
|
|
97
|
+
if (rows.length === 0) {
|
|
98
|
+
throw new Error(`Club ${payload.clubId} does not exist`);
|
|
99
|
+
}
|
|
100
|
+
const club = rows[0];
|
|
101
|
+
(0, helpers_1.assertAssetMatches)(payment.asset, club.asset);
|
|
102
|
+
if (payment.amount !== club.join_price) {
|
|
103
|
+
throw new Error(`Club join price is ${club.join_price} ${club.asset}`);
|
|
104
|
+
}
|
|
105
|
+
const members = await state.adapter.query('SELECT * FROM fan_club_members WHERE club_id = ? AND account = ?', [payload.clubId, account]);
|
|
106
|
+
const renewals = members.length > 0 ? Number(members[0].renewals) + 1 : 1;
|
|
107
|
+
const points = members.length > 0 ? Number(members[0].points) : 0;
|
|
108
|
+
await state.adapter.query(`INSERT INTO fan_club_members (club_id, account, status, renewals, points, created_at, updated_at)
|
|
109
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
110
|
+
ON CONFLICT(club_id, account)
|
|
111
|
+
DO UPDATE SET status = excluded.status, renewals = excluded.renewals, updated_at = excluded.updated_at`, [payload.clubId, account, 'active', renewals, points, new Date(), new Date()]);
|
|
112
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'joinClub', payload, {
|
|
113
|
+
action: 'fan_club_joined',
|
|
114
|
+
data: {
|
|
115
|
+
clubId: payload.clubId,
|
|
116
|
+
account,
|
|
117
|
+
renewals
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const recordEngagement = async (payload, ctx) => {
|
|
122
|
+
const owner = (0, helpers_1.requireSender)(ctx);
|
|
123
|
+
const clubRows = await state.adapter.query('SELECT * FROM fan_clubs WHERE club_id = ?', [payload.clubId]);
|
|
124
|
+
const memberRows = await state.adapter.query('SELECT * FROM fan_club_members WHERE club_id = ? AND account = ?', [payload.clubId, payload.account]);
|
|
125
|
+
if (clubRows.length === 0 || memberRows.length === 0) {
|
|
126
|
+
throw new Error('Club or member does not exist');
|
|
127
|
+
}
|
|
128
|
+
const club = clubRows[0];
|
|
129
|
+
if (club.owner !== owner) {
|
|
130
|
+
throw new Error('Only the club owner can record engagement');
|
|
131
|
+
}
|
|
132
|
+
const member = memberRows[0];
|
|
133
|
+
const nextPoints = Number(member.points) + payload.points;
|
|
134
|
+
await state.adapter.query('UPDATE fan_club_members SET points = ?, updated_at = ? WHERE club_id = ? AND account = ?', [nextPoints, new Date(), payload.clubId, payload.account]);
|
|
135
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'recordEngagement', payload, {
|
|
136
|
+
action: 'fan_club_progress_recorded',
|
|
137
|
+
data: {
|
|
138
|
+
clubId: payload.clubId,
|
|
139
|
+
account: payload.account,
|
|
140
|
+
totalPoints: nextPoints,
|
|
141
|
+
note: payload.note || ''
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
const redeemPerk = async (payload, ctx) => {
|
|
146
|
+
const account = (0, helpers_1.requireSender)(ctx);
|
|
147
|
+
const clubRows = await state.adapter.query('SELECT * FROM fan_clubs WHERE club_id = ?', [payload.clubId]);
|
|
148
|
+
const memberRows = await state.adapter.query('SELECT * FROM fan_club_members WHERE club_id = ? AND account = ?', [payload.clubId, account]);
|
|
149
|
+
if (clubRows.length === 0 || memberRows.length === 0) {
|
|
150
|
+
throw new Error('Club or member does not exist');
|
|
151
|
+
}
|
|
152
|
+
const club = clubRows[0];
|
|
153
|
+
const member = memberRows[0];
|
|
154
|
+
const perks = (0, helpers_1.parseJson)(club.perks_json, []);
|
|
155
|
+
const perk = perks.find(candidate => candidate.perkId === payload.perkId);
|
|
156
|
+
if (!perk) {
|
|
157
|
+
throw new Error('Perk does not exist');
|
|
158
|
+
}
|
|
159
|
+
if (Number(member.points) < perk.minPoints) {
|
|
160
|
+
throw new Error('Member has not unlocked this perk');
|
|
161
|
+
}
|
|
162
|
+
const existing = await state.adapter.query('SELECT id FROM fan_club_redemptions WHERE club_id = ? AND account = ? AND perk_id = ?', [payload.clubId, account, payload.perkId]);
|
|
163
|
+
if (existing.length > 0) {
|
|
164
|
+
throw new Error('Perk already redeemed');
|
|
165
|
+
}
|
|
166
|
+
await state.adapter.query('INSERT INTO fan_club_redemptions (club_id, account, perk_id, created_at) VALUES (?, ?, ?, ?)', [payload.clubId, account, payload.perkId, new Date()]);
|
|
167
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'redeemPerk', payload, {
|
|
168
|
+
action: 'fan_club_perk_redeemed',
|
|
169
|
+
data: {
|
|
170
|
+
clubId: payload.clubId,
|
|
171
|
+
account,
|
|
172
|
+
perkId: payload.perkId,
|
|
173
|
+
title: perk.title
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
return (0, contract_1.defineContract)({
|
|
178
|
+
name,
|
|
179
|
+
hooks: {
|
|
180
|
+
create: async ({ adapter }) => {
|
|
181
|
+
state.adapter = adapter;
|
|
182
|
+
await initialize();
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
actions: {
|
|
186
|
+
createClub: (0, contract_1.action)(createClub, { schema: createClubSchema, trigger: 'custom_json' }),
|
|
187
|
+
joinClub: (0, contract_1.action)(joinClub, { schema: clubIdSchema, trigger: ['transfer', 'recurrent_transfer'] }),
|
|
188
|
+
recordEngagement: (0, contract_1.action)(recordEngagement, { schema: progressSchema, trigger: 'custom_json' }),
|
|
189
|
+
redeemPerk: (0, contract_1.action)(redeemPerk, { schema: redeemSchema, trigger: 'custom_json' })
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=fanclub.contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fanclub.contract.js","sourceRoot":"","sources":["../../src/contracts/fanclub.contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGiftCardContract = createGiftCardContract;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const contract_1 = require("./contract");
|
|
6
|
+
const helpers_1 = require("./helpers");
|
|
7
|
+
const DEFAULT_NAME = 'giftcards';
|
|
8
|
+
function createGiftCardContract(options = {}) {
|
|
9
|
+
const name = options.name || DEFAULT_NAME;
|
|
10
|
+
const state = (0, helpers_1.createContractState)();
|
|
11
|
+
const issueSchema = zod_1.z.object({
|
|
12
|
+
code: helpers_1.identifierSchema,
|
|
13
|
+
recipient: zod_1.z.string().min(3).max(32).optional(),
|
|
14
|
+
message: zod_1.z.string().max(280).optional(),
|
|
15
|
+
expiresAt: zod_1.z.string().optional()
|
|
16
|
+
});
|
|
17
|
+
const redeemSchema = zod_1.z.object({
|
|
18
|
+
code: helpers_1.identifierSchema,
|
|
19
|
+
amount: helpers_1.amountSchema.optional()
|
|
20
|
+
});
|
|
21
|
+
const initialize = async () => {
|
|
22
|
+
await (0, helpers_1.initializeTables)(state.adapter, [
|
|
23
|
+
`
|
|
24
|
+
CREATE TABLE IF NOT EXISTS gift_cards (
|
|
25
|
+
code TEXT PRIMARY KEY,
|
|
26
|
+
issuer TEXT NOT NULL,
|
|
27
|
+
purchaser TEXT NOT NULL,
|
|
28
|
+
recipient TEXT,
|
|
29
|
+
amount TEXT NOT NULL,
|
|
30
|
+
remaining_amount TEXT NOT NULL,
|
|
31
|
+
asset TEXT NOT NULL,
|
|
32
|
+
message TEXT,
|
|
33
|
+
expires_at DATETIME,
|
|
34
|
+
status TEXT NOT NULL,
|
|
35
|
+
created_at DATETIME NOT NULL
|
|
36
|
+
)
|
|
37
|
+
`,
|
|
38
|
+
`
|
|
39
|
+
CREATE TABLE IF NOT EXISTS gift_card_redemptions (
|
|
40
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
41
|
+
code TEXT NOT NULL,
|
|
42
|
+
redeemer TEXT NOT NULL,
|
|
43
|
+
amount TEXT NOT NULL,
|
|
44
|
+
created_at DATETIME NOT NULL
|
|
45
|
+
)
|
|
46
|
+
`
|
|
47
|
+
]);
|
|
48
|
+
};
|
|
49
|
+
const issueGiftCard = async (payload, ctx) => {
|
|
50
|
+
const issuer = (0, helpers_1.requireSender)(ctx);
|
|
51
|
+
const payment = (0, helpers_1.getIncomingPayment)(ctx);
|
|
52
|
+
const existing = await state.adapter.query('SELECT code FROM gift_cards WHERE code = ?', [payload.code]);
|
|
53
|
+
if (existing.length > 0) {
|
|
54
|
+
throw new Error(`Gift card ${payload.code} already exists`);
|
|
55
|
+
}
|
|
56
|
+
const expiresAt = (0, helpers_1.parseDateValue)(payload.expiresAt);
|
|
57
|
+
await state.adapter.query(`INSERT INTO gift_cards (
|
|
58
|
+
code, issuer, purchaser, recipient, amount, remaining_amount, asset, message, expires_at, status, created_at
|
|
59
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
60
|
+
payload.code,
|
|
61
|
+
issuer,
|
|
62
|
+
issuer,
|
|
63
|
+
payload.recipient || null,
|
|
64
|
+
payment.amount,
|
|
65
|
+
payment.amount,
|
|
66
|
+
payment.asset,
|
|
67
|
+
payload.message || '',
|
|
68
|
+
expiresAt,
|
|
69
|
+
'active',
|
|
70
|
+
new Date()
|
|
71
|
+
]);
|
|
72
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'issueGiftCard', payload, {
|
|
73
|
+
action: 'gift_card_issued',
|
|
74
|
+
data: {
|
|
75
|
+
code: payload.code,
|
|
76
|
+
issuer,
|
|
77
|
+
amount: payment.amount,
|
|
78
|
+
asset: payment.asset,
|
|
79
|
+
recipient: payload.recipient || null
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const redeemGiftCard = async (payload, ctx) => {
|
|
84
|
+
const redeemer = (0, helpers_1.requireSender)(ctx);
|
|
85
|
+
const rows = await state.adapter.query('SELECT * FROM gift_cards WHERE code = ?', [payload.code]);
|
|
86
|
+
if (rows.length === 0) {
|
|
87
|
+
throw new Error(`Gift card ${payload.code} does not exist`);
|
|
88
|
+
}
|
|
89
|
+
const card = rows[0];
|
|
90
|
+
if (card.status !== 'active') {
|
|
91
|
+
throw new Error('Gift card is not active');
|
|
92
|
+
}
|
|
93
|
+
const expiresAt = (0, helpers_1.parseDateValue)(card.expires_at);
|
|
94
|
+
if (expiresAt && expiresAt < new Date()) {
|
|
95
|
+
throw new Error('Gift card has expired');
|
|
96
|
+
}
|
|
97
|
+
if (card.recipient && card.recipient !== redeemer) {
|
|
98
|
+
throw new Error('Gift card is restricted to a different recipient');
|
|
99
|
+
}
|
|
100
|
+
const amount = payload.amount || card.remaining_amount;
|
|
101
|
+
if ((0, helpers_1.toBigNumber)(amount).gt(card.remaining_amount)) {
|
|
102
|
+
throw new Error('Redemption amount exceeds card balance');
|
|
103
|
+
}
|
|
104
|
+
const remaining = (0, helpers_1.toBigNumber)(card.remaining_amount).minus(amount);
|
|
105
|
+
const status = remaining.eq(0)
|
|
106
|
+
? 'redeemed'
|
|
107
|
+
: 'active';
|
|
108
|
+
await state.adapter.query('INSERT INTO gift_card_redemptions (code, redeemer, amount, created_at) VALUES (?, ?, ?, ?)', [payload.code, redeemer, amount, new Date()]);
|
|
109
|
+
await state.adapter.query('UPDATE gift_cards SET remaining_amount = ?, status = ? WHERE code = ?', [remaining.toFixed(), status, payload.code]);
|
|
110
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'redeemGiftCard', payload, {
|
|
111
|
+
action: 'gift_card_redeemed',
|
|
112
|
+
data: {
|
|
113
|
+
code: payload.code,
|
|
114
|
+
redeemer,
|
|
115
|
+
amount,
|
|
116
|
+
asset: card.asset,
|
|
117
|
+
remainingAmount: remaining.toFixed()
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const cancelGiftCard = async (payload, ctx) => {
|
|
122
|
+
const issuer = (0, helpers_1.requireSender)(ctx);
|
|
123
|
+
const rows = await state.adapter.query('SELECT * FROM gift_cards WHERE code = ?', [payload.code]);
|
|
124
|
+
if (rows.length === 0) {
|
|
125
|
+
throw new Error(`Gift card ${payload.code} does not exist`);
|
|
126
|
+
}
|
|
127
|
+
const card = rows[0];
|
|
128
|
+
if (card.issuer !== issuer) {
|
|
129
|
+
throw new Error('Only the issuer can cancel this gift card');
|
|
130
|
+
}
|
|
131
|
+
if ((0, helpers_1.toBigNumber)(card.remaining_amount).lt(card.amount)) {
|
|
132
|
+
throw new Error('Gift cards with redemptions cannot be cancelled');
|
|
133
|
+
}
|
|
134
|
+
await state.adapter.query('UPDATE gift_cards SET status = ? WHERE code = ?', ['cancelled', payload.code]);
|
|
135
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'cancelGiftCard', payload, {
|
|
136
|
+
action: 'gift_card_cancelled',
|
|
137
|
+
data: {
|
|
138
|
+
code: payload.code,
|
|
139
|
+
issuer
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
return (0, contract_1.defineContract)({
|
|
144
|
+
name,
|
|
145
|
+
hooks: {
|
|
146
|
+
create: async ({ adapter }) => {
|
|
147
|
+
state.adapter = adapter;
|
|
148
|
+
await initialize();
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
actions: {
|
|
152
|
+
issueGiftCard: (0, contract_1.action)(issueGiftCard, { schema: issueSchema, trigger: ['transfer', 'recurrent_transfer'] }),
|
|
153
|
+
redeemGiftCard: (0, contract_1.action)(redeemGiftCard, { schema: redeemSchema, trigger: 'custom_json' }),
|
|
154
|
+
cancelGiftCard: (0, contract_1.action)(cancelGiftCard, { schema: zod_1.z.object({ code: helpers_1.identifierSchema }), trigger: 'custom_json' })
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=giftcard.contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"giftcard.contract.js","sourceRoot":"","sources":["../../src/contracts/giftcard.contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGrantRoundsContract = createGrantRoundsContract;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const contract_1 = require("./contract");
|
|
6
|
+
const helpers_1 = require("./helpers");
|
|
7
|
+
const DEFAULT_NAME = 'grantrounds';
|
|
8
|
+
function createGrantRoundsContract(options = {}) {
|
|
9
|
+
const name = options.name || DEFAULT_NAME;
|
|
10
|
+
const state = (0, helpers_1.createContractState)();
|
|
11
|
+
const createRoundSchema = zod_1.z.object({
|
|
12
|
+
roundId: helpers_1.identifierSchema,
|
|
13
|
+
title: zod_1.z.string().min(3).max(140),
|
|
14
|
+
asset: helpers_1.assetSchema,
|
|
15
|
+
closesAt: zod_1.z.string(),
|
|
16
|
+
metadata: zod_1.z.record(zod_1.z.any()).optional()
|
|
17
|
+
});
|
|
18
|
+
const idSchema = zod_1.z.object({
|
|
19
|
+
roundId: helpers_1.identifierSchema
|
|
20
|
+
});
|
|
21
|
+
const projectSchema = zod_1.z.object({
|
|
22
|
+
roundId: helpers_1.identifierSchema,
|
|
23
|
+
projectId: helpers_1.identifierSchema,
|
|
24
|
+
title: zod_1.z.string().min(3).max(140),
|
|
25
|
+
recipient: zod_1.z.string().min(3).max(32),
|
|
26
|
+
summary: zod_1.z.string().max(500).optional()
|
|
27
|
+
});
|
|
28
|
+
const donateSchema = zod_1.z.object({
|
|
29
|
+
roundId: helpers_1.identifierSchema,
|
|
30
|
+
projectId: helpers_1.identifierSchema
|
|
31
|
+
});
|
|
32
|
+
const withdrawSchema = zod_1.z.object({
|
|
33
|
+
roundId: helpers_1.identifierSchema,
|
|
34
|
+
projectId: helpers_1.identifierSchema
|
|
35
|
+
});
|
|
36
|
+
const initialize = async () => {
|
|
37
|
+
await (0, helpers_1.initializeTables)(state.adapter, [
|
|
38
|
+
`
|
|
39
|
+
CREATE TABLE IF NOT EXISTS grant_rounds (
|
|
40
|
+
round_id TEXT PRIMARY KEY,
|
|
41
|
+
owner TEXT NOT NULL,
|
|
42
|
+
title TEXT NOT NULL,
|
|
43
|
+
asset TEXT NOT NULL,
|
|
44
|
+
matching_pool TEXT NOT NULL,
|
|
45
|
+
status TEXT NOT NULL,
|
|
46
|
+
closes_at DATETIME NOT NULL,
|
|
47
|
+
metadata TEXT,
|
|
48
|
+
created_at DATETIME NOT NULL,
|
|
49
|
+
finalized_at DATETIME
|
|
50
|
+
)
|
|
51
|
+
`,
|
|
52
|
+
`
|
|
53
|
+
CREATE TABLE IF NOT EXISTS grant_projects (
|
|
54
|
+
project_id TEXT PRIMARY KEY,
|
|
55
|
+
round_id TEXT NOT NULL,
|
|
56
|
+
title TEXT NOT NULL,
|
|
57
|
+
recipient TEXT NOT NULL,
|
|
58
|
+
summary TEXT,
|
|
59
|
+
donations_total TEXT NOT NULL,
|
|
60
|
+
matching_award TEXT NOT NULL,
|
|
61
|
+
withdrawn INTEGER NOT NULL,
|
|
62
|
+
created_at DATETIME NOT NULL
|
|
63
|
+
)
|
|
64
|
+
`,
|
|
65
|
+
`
|
|
66
|
+
CREATE TABLE IF NOT EXISTS grant_donations (
|
|
67
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
68
|
+
round_id TEXT NOT NULL,
|
|
69
|
+
project_id TEXT NOT NULL,
|
|
70
|
+
donor TEXT NOT NULL,
|
|
71
|
+
amount TEXT NOT NULL,
|
|
72
|
+
asset TEXT NOT NULL,
|
|
73
|
+
created_at DATETIME NOT NULL
|
|
74
|
+
)
|
|
75
|
+
`
|
|
76
|
+
]);
|
|
77
|
+
};
|
|
78
|
+
const createRound = async (payload, ctx) => {
|
|
79
|
+
const owner = (0, helpers_1.requireSender)(ctx);
|
|
80
|
+
const existing = await state.adapter.query('SELECT round_id FROM grant_rounds WHERE round_id = ?', [payload.roundId]);
|
|
81
|
+
if (existing.length > 0) {
|
|
82
|
+
throw new Error(`Grant round ${payload.roundId} already exists`);
|
|
83
|
+
}
|
|
84
|
+
const closesAt = (0, helpers_1.parseDateValue)(payload.closesAt);
|
|
85
|
+
if (!closesAt || closesAt <= new Date()) {
|
|
86
|
+
throw new Error('Grant round close time must be in the future');
|
|
87
|
+
}
|
|
88
|
+
await state.adapter.query(`INSERT INTO grant_rounds (
|
|
89
|
+
round_id, owner, title, asset, matching_pool, status, closes_at, metadata, created_at, finalized_at
|
|
90
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [payload.roundId, owner, payload.title, payload.asset, '0', 'open', closesAt, JSON.stringify(payload.metadata || {}), new Date(), null]);
|
|
91
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'createRound', payload, {
|
|
92
|
+
action: 'grant_round_created',
|
|
93
|
+
data: {
|
|
94
|
+
roundId: payload.roundId,
|
|
95
|
+
owner
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const fundRound = async (payload, ctx) => {
|
|
100
|
+
const owner = (0, helpers_1.requireSender)(ctx);
|
|
101
|
+
const payment = (0, helpers_1.getIncomingPayment)(ctx);
|
|
102
|
+
const rows = await state.adapter.query('SELECT * FROM grant_rounds WHERE round_id = ?', [payload.roundId]);
|
|
103
|
+
if (rows.length === 0) {
|
|
104
|
+
throw new Error(`Grant round ${payload.roundId} does not exist`);
|
|
105
|
+
}
|
|
106
|
+
const round = rows[0];
|
|
107
|
+
if (round.owner !== owner) {
|
|
108
|
+
throw new Error('Only the round owner can fund the matching pool');
|
|
109
|
+
}
|
|
110
|
+
(0, helpers_1.assertAssetMatches)(payment.asset, round.asset);
|
|
111
|
+
const matchingPool = (0, helpers_1.toBigNumber)(round.matching_pool).plus(payment.amount);
|
|
112
|
+
await state.adapter.query('UPDATE grant_rounds SET matching_pool = ? WHERE round_id = ?', [matchingPool.toFixed(), payload.roundId]);
|
|
113
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'fundRound', payload, {
|
|
114
|
+
action: 'grant_round_funded',
|
|
115
|
+
data: {
|
|
116
|
+
roundId: payload.roundId,
|
|
117
|
+
matchingPool: matchingPool.toFixed()
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const submitProject = async (payload, ctx) => {
|
|
122
|
+
const sender = (0, helpers_1.requireSender)(ctx);
|
|
123
|
+
const roundRows = await state.adapter.query('SELECT * FROM grant_rounds WHERE round_id = ?', [payload.roundId]);
|
|
124
|
+
if (roundRows.length === 0) {
|
|
125
|
+
throw new Error(`Grant round ${payload.roundId} does not exist`);
|
|
126
|
+
}
|
|
127
|
+
const existing = await state.adapter.query('SELECT project_id FROM grant_projects WHERE project_id = ?', [payload.projectId]);
|
|
128
|
+
if (existing.length > 0) {
|
|
129
|
+
throw new Error(`Project ${payload.projectId} already exists`);
|
|
130
|
+
}
|
|
131
|
+
await state.adapter.query(`INSERT INTO grant_projects (
|
|
132
|
+
project_id, round_id, title, recipient, summary, donations_total, matching_award, withdrawn, created_at
|
|
133
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [payload.projectId, payload.roundId, payload.title, payload.recipient, payload.summary || '', '0', '0', 0, new Date()]);
|
|
134
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'submitProject', payload, {
|
|
135
|
+
action: 'grant_project_submitted',
|
|
136
|
+
data: {
|
|
137
|
+
roundId: payload.roundId,
|
|
138
|
+
projectId: payload.projectId,
|
|
139
|
+
submitter: sender,
|
|
140
|
+
recipient: payload.recipient
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
const donateToProject = async (payload, ctx) => {
|
|
145
|
+
const donor = (0, helpers_1.requireSender)(ctx);
|
|
146
|
+
const payment = (0, helpers_1.getIncomingPayment)(ctx);
|
|
147
|
+
const roundRows = await state.adapter.query('SELECT * FROM grant_rounds WHERE round_id = ?', [payload.roundId]);
|
|
148
|
+
const projectRows = await state.adapter.query('SELECT * FROM grant_projects WHERE project_id = ? AND round_id = ?', [payload.projectId, payload.roundId]);
|
|
149
|
+
if (roundRows.length === 0 || projectRows.length === 0) {
|
|
150
|
+
throw new Error('Grant round or project does not exist');
|
|
151
|
+
}
|
|
152
|
+
const round = roundRows[0];
|
|
153
|
+
const project = projectRows[0];
|
|
154
|
+
if (round.status !== 'open') {
|
|
155
|
+
throw new Error('Grant round is not open');
|
|
156
|
+
}
|
|
157
|
+
if (((0, helpers_1.parseDateValue)(round.closes_at) || new Date()) <= new Date()) {
|
|
158
|
+
throw new Error('Grant round has already closed');
|
|
159
|
+
}
|
|
160
|
+
(0, helpers_1.assertAssetMatches)(payment.asset, round.asset);
|
|
161
|
+
const total = (0, helpers_1.toBigNumber)(project.donations_total).plus(payment.amount);
|
|
162
|
+
await state.adapter.query('INSERT INTO grant_donations (round_id, project_id, donor, amount, asset, created_at) VALUES (?, ?, ?, ?, ?, ?)', [payload.roundId, payload.projectId, donor, payment.amount, payment.asset, new Date()]);
|
|
163
|
+
await state.adapter.query('UPDATE grant_projects SET donations_total = ? WHERE project_id = ?', [total.toFixed(), payload.projectId]);
|
|
164
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'donateToProject', payload, {
|
|
165
|
+
action: 'grant_project_donated',
|
|
166
|
+
data: {
|
|
167
|
+
roundId: payload.roundId,
|
|
168
|
+
projectId: payload.projectId,
|
|
169
|
+
donor,
|
|
170
|
+
amount: payment.amount
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
const finalizeRound = async (payload, ctx) => {
|
|
175
|
+
const rows = await state.adapter.query('SELECT * FROM grant_rounds WHERE round_id = ?', [payload.roundId]);
|
|
176
|
+
if (rows.length === 0) {
|
|
177
|
+
throw new Error(`Grant round ${payload.roundId} does not exist`);
|
|
178
|
+
}
|
|
179
|
+
const round = rows[0];
|
|
180
|
+
if (round.status !== 'open') {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (ctx.trigger !== 'time') {
|
|
184
|
+
const owner = (0, helpers_1.requireSender)(ctx);
|
|
185
|
+
if (owner !== round.owner) {
|
|
186
|
+
throw new Error('Only the round owner can finalize the round');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (((0, helpers_1.parseDateValue)(round.closes_at) || new Date()) > new Date()) {
|
|
190
|
+
throw new Error('Grant round cannot be finalized before close');
|
|
191
|
+
}
|
|
192
|
+
const projects = await state.adapter.query('SELECT * FROM grant_projects WHERE round_id = ?', [payload.roundId]);
|
|
193
|
+
const donorCounts = new Map();
|
|
194
|
+
let totalWeight = 0;
|
|
195
|
+
for (const project of projects) {
|
|
196
|
+
const rowsForProject = await state.adapter.query('SELECT COUNT(DISTINCT donor) AS count FROM grant_donations WHERE round_id = ? AND project_id = ?', [payload.roundId, project.project_id]);
|
|
197
|
+
const count = Number(rowsForProject[0]?.count || 0);
|
|
198
|
+
donorCounts.set(project.project_id, count);
|
|
199
|
+
totalWeight += count;
|
|
200
|
+
}
|
|
201
|
+
for (const project of projects) {
|
|
202
|
+
const count = donorCounts.get(project.project_id) || 0;
|
|
203
|
+
const award = totalWeight === 0
|
|
204
|
+
? (0, helpers_1.toBigNumber)(0)
|
|
205
|
+
: (0, helpers_1.toBigNumber)(round.matching_pool).multipliedBy(count).dividedBy(totalWeight).decimalPlaces(8, 1);
|
|
206
|
+
await state.adapter.query('UPDATE grant_projects SET matching_award = ? WHERE project_id = ?', [award.toFixed(), project.project_id]);
|
|
207
|
+
}
|
|
208
|
+
await state.adapter.query('UPDATE grant_rounds SET status = ?, finalized_at = ? WHERE round_id = ?', ['finalized', new Date(), payload.roundId]);
|
|
209
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'finalizeRound', payload, {
|
|
210
|
+
action: 'grant_round_finalized',
|
|
211
|
+
data: {
|
|
212
|
+
roundId: payload.roundId,
|
|
213
|
+
projectCount: projects.length
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
const withdrawGrant = async (payload, ctx) => {
|
|
218
|
+
const account = (0, helpers_1.requireSender)(ctx);
|
|
219
|
+
const roundRows = await state.adapter.query('SELECT * FROM grant_rounds WHERE round_id = ?', [payload.roundId]);
|
|
220
|
+
const projectRows = await state.adapter.query('SELECT * FROM grant_projects WHERE project_id = ? AND round_id = ?', [payload.projectId, payload.roundId]);
|
|
221
|
+
if (roundRows.length === 0 || projectRows.length === 0) {
|
|
222
|
+
throw new Error('Grant round or project does not exist');
|
|
223
|
+
}
|
|
224
|
+
const round = roundRows[0];
|
|
225
|
+
const project = projectRows[0];
|
|
226
|
+
if (round.status !== 'finalized') {
|
|
227
|
+
throw new Error('Grant round is not finalized');
|
|
228
|
+
}
|
|
229
|
+
if (project.recipient !== account) {
|
|
230
|
+
throw new Error('Only the project recipient can withdraw the grant');
|
|
231
|
+
}
|
|
232
|
+
if (project.withdrawn) {
|
|
233
|
+
throw new Error('Grant already withdrawn');
|
|
234
|
+
}
|
|
235
|
+
await state.adapter.query('UPDATE grant_projects SET withdrawn = ? WHERE project_id = ?', [1, payload.projectId]);
|
|
236
|
+
await (0, helpers_1.emitContractEvent)(state.adapter, name, 'withdrawGrant', payload, {
|
|
237
|
+
action: 'grant_withdrawal_requested',
|
|
238
|
+
data: {
|
|
239
|
+
roundId: payload.roundId,
|
|
240
|
+
projectId: payload.projectId,
|
|
241
|
+
recipient: account,
|
|
242
|
+
totalAmount: (0, helpers_1.toBigNumber)(project.donations_total).plus(project.matching_award).toFixed(),
|
|
243
|
+
asset: round.asset
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
};
|
|
247
|
+
return (0, contract_1.defineContract)({
|
|
248
|
+
name,
|
|
249
|
+
hooks: {
|
|
250
|
+
create: async ({ adapter }) => {
|
|
251
|
+
state.adapter = adapter;
|
|
252
|
+
await initialize();
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
actions: {
|
|
256
|
+
createRound: (0, contract_1.action)(createRound, { schema: createRoundSchema, trigger: 'custom_json' }),
|
|
257
|
+
fundRound: (0, contract_1.action)(fundRound, { schema: idSchema, trigger: ['transfer', 'recurrent_transfer'] }),
|
|
258
|
+
submitProject: (0, contract_1.action)(submitProject, { schema: projectSchema, trigger: 'custom_json' }),
|
|
259
|
+
donateToProject: (0, contract_1.action)(donateToProject, { schema: donateSchema, trigger: ['transfer', 'recurrent_transfer'] }),
|
|
260
|
+
finalizeRound: (0, contract_1.action)(finalizeRound, { schema: idSchema, trigger: ['custom_json', 'time'] }),
|
|
261
|
+
withdrawGrant: (0, contract_1.action)(withdrawGrant, { schema: withdrawSchema, trigger: 'custom_json' })
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=grantrounds.contract.js.map
|