@bamzzstudio/loka-sdk 0.1.0

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/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Loka SDK
2
+
3
+ Reusable SDK, ABIs, and deterministic invoice-agent helpers for Loka, a MiniPay-first merchant payment app on Celo.
4
+
5
+ ## Packages
6
+
7
+ - `@bamzzstudio/loka-sdk`: Umbrella SDK.
8
+ - `@bamzzstudio/loka-core`: Invoice IDs, amount parsing, Celo constants.
9
+ - `@bamzzstudio/loka-contracts`: Ledger ABI and viem payment helpers.
10
+ - `@bamzzstudio/loka-agent`: Deterministic payment request agent.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @bamzzstudio/loka-sdk
16
+ ```
17
+
18
+ ## Example
19
+
20
+ ```ts
21
+ import { preparePaymentRequest } from "@bamzzstudio/loka-sdk";
22
+
23
+ const result = preparePaymentRequest({
24
+ amount: "2.50",
25
+ tokenSymbol: "USDm",
26
+ note: "Lunch order",
27
+ customer: "Amina",
28
+ });
29
+ ```
@@ -0,0 +1,182 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.24;
3
+
4
+ import "@openzeppelin/contracts/access/Ownable.sol";
5
+ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
+
8
+ /// @title LokaPayLedger
9
+ /// @notice Merchant payment ledger for Celo native and ERC-20 stablecoin payments.
10
+ contract LokaPayLedger is Ownable {
11
+ using SafeERC20 for IERC20;
12
+
13
+ uint256 public constant BPS_DENOMINATOR = 10_000;
14
+ uint256 public constant MAX_FEE_BPS = 500;
15
+
16
+ address payable public treasury;
17
+ uint256 public feeBps;
18
+ uint256 public totalPayments;
19
+
20
+ struct Receipt {
21
+ bytes32 invoiceId;
22
+ address payer;
23
+ address merchant;
24
+ address token;
25
+ uint256 amount;
26
+ uint256 fee;
27
+ bytes32 memoHash;
28
+ uint64 paidAt;
29
+ }
30
+
31
+ mapping(uint256 receiptId => Receipt receipt) public receipts;
32
+ mapping(bytes32 invoiceId => uint256 count) public invoicePaymentCount;
33
+
34
+ event LokaPayment(
35
+ uint256 indexed receiptId,
36
+ bytes32 indexed invoiceId,
37
+ address indexed merchant,
38
+ address payer,
39
+ address token,
40
+ uint256 amount,
41
+ uint256 fee,
42
+ bytes32 memoHash
43
+ );
44
+ event TreasuryUpdated(address indexed oldTreasury, address indexed newTreasury);
45
+ event FeeBpsUpdated(uint256 oldFeeBps, uint256 newFeeBps);
46
+
47
+ constructor(
48
+ address initialOwner_,
49
+ address payable treasury_,
50
+ uint256 feeBps_
51
+ ) Ownable(initialOwner_) {
52
+ require(initialOwner_ != address(0), "OWNER_ZERO");
53
+ require(feeBps_ <= MAX_FEE_BPS, "FEE_TOO_HIGH");
54
+
55
+ treasury = treasury_ == address(0) ? payable(initialOwner_) : treasury_;
56
+ feeBps = feeBps_;
57
+ }
58
+
59
+ function payNative(
60
+ bytes32 invoiceId,
61
+ address payable merchant,
62
+ bytes32 memoHash
63
+ ) external payable returns (uint256 receiptId) {
64
+ require(msg.value > 0, "AMOUNT_ZERO");
65
+ require(invoiceId != bytes32(0), "INVOICE_EMPTY");
66
+ require(merchant != address(0), "MERCHANT_ZERO");
67
+
68
+ uint256 fee = _feeFor(msg.value);
69
+ uint256 payout = msg.value - fee;
70
+
71
+ receiptId = _recordPayment(
72
+ invoiceId,
73
+ msg.sender,
74
+ merchant,
75
+ address(0),
76
+ msg.value,
77
+ fee,
78
+ memoHash
79
+ );
80
+
81
+ if (fee > 0) {
82
+ (bool feeOk, ) = treasury.call{value: fee}("");
83
+ require(feeOk, "FEE_TRANSFER_FAILED");
84
+ }
85
+
86
+ (bool payOk, ) = merchant.call{value: payout}("");
87
+ require(payOk, "PAYMENT_TRANSFER_FAILED");
88
+ }
89
+
90
+ function payToken(
91
+ bytes32 invoiceId,
92
+ address merchant,
93
+ address token,
94
+ uint256 amount,
95
+ bytes32 memoHash
96
+ ) external returns (uint256 receiptId) {
97
+ require(amount > 0, "AMOUNT_ZERO");
98
+ require(invoiceId != bytes32(0), "INVOICE_EMPTY");
99
+ require(merchant != address(0), "MERCHANT_ZERO");
100
+ require(token != address(0), "TOKEN_ZERO");
101
+
102
+ IERC20 paymentToken = IERC20(token);
103
+ uint256 fee = _feeFor(amount);
104
+ uint256 payout = amount - fee;
105
+
106
+ paymentToken.safeTransferFrom(msg.sender, address(this), amount);
107
+
108
+ receiptId = _recordPayment(
109
+ invoiceId,
110
+ msg.sender,
111
+ merchant,
112
+ token,
113
+ amount,
114
+ fee,
115
+ memoHash
116
+ );
117
+
118
+ if (fee > 0) {
119
+ paymentToken.safeTransfer(treasury, fee);
120
+ }
121
+ paymentToken.safeTransfer(merchant, payout);
122
+ }
123
+
124
+ function setTreasury(address payable newTreasury) external onlyOwner {
125
+ require(newTreasury != address(0), "TREASURY_ZERO");
126
+
127
+ address oldTreasury = treasury;
128
+ treasury = newTreasury;
129
+
130
+ emit TreasuryUpdated(oldTreasury, newTreasury);
131
+ }
132
+
133
+ function setFeeBps(uint256 newFeeBps) external onlyOwner {
134
+ require(newFeeBps <= MAX_FEE_BPS, "FEE_TOO_HIGH");
135
+
136
+ uint256 oldFeeBps = feeBps;
137
+ feeBps = newFeeBps;
138
+
139
+ emit FeeBpsUpdated(oldFeeBps, newFeeBps);
140
+ }
141
+
142
+ function _recordPayment(
143
+ bytes32 invoiceId,
144
+ address payer,
145
+ address merchant,
146
+ address token,
147
+ uint256 amount,
148
+ uint256 fee,
149
+ bytes32 memoHash
150
+ ) internal returns (uint256 receiptId) {
151
+ receiptId = ++totalPayments;
152
+ invoicePaymentCount[invoiceId] += 1;
153
+
154
+ receipts[receiptId] = Receipt({
155
+ invoiceId: invoiceId,
156
+ payer: payer,
157
+ merchant: merchant,
158
+ token: token,
159
+ amount: amount,
160
+ fee: fee,
161
+ memoHash: memoHash,
162
+ paidAt: uint64(block.timestamp)
163
+ });
164
+
165
+ emit LokaPayment(
166
+ receiptId,
167
+ invoiceId,
168
+ merchant,
169
+ payer,
170
+ token,
171
+ amount,
172
+ fee,
173
+ memoHash
174
+ );
175
+ }
176
+
177
+ function _feeFor(uint256 amount) internal view returns (uint256) {
178
+ return (amount * feeBps) / BPS_DENOMINATOR;
179
+ }
180
+
181
+ receive() external payable {}
182
+ }
@@ -0,0 +1,4 @@
1
+ export * from "@bamzzstudio/loka-agent";
2
+ export * from "@bamzzstudio/loka-contracts";
3
+ export * from "@bamzzstudio/loka-core";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "@bamzzstudio/loka-agent";
2
+ export * from "@bamzzstudio/loka-contracts";
3
+ export * from "@bamzzstudio/loka-core";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,64 @@
1
+ # Remix Deployment
2
+
3
+ This guide deploys `LokaPayLedger.sol` from Remix to Celo.
4
+
5
+ ## Compiler
6
+
7
+ - Solidity: `0.8.24` or newer compatible `0.8.x`
8
+ - Enable optimization: `200` runs
9
+
10
+ ## Constructor Values
11
+
12
+ `LokaPayLedger` constructor:
13
+
14
+ ```solidity
15
+ constructor(
16
+ address initialOwner_,
17
+ address payable treasury_,
18
+ uint256 feeBps_
19
+ )
20
+ ```
21
+
22
+ Recommended values:
23
+
24
+ - `initialOwner_`: owner wallet that can update fee and treasury
25
+ - `treasury_`: wallet that receives protocol fees, or zero address to use `initialOwner_`
26
+ - `feeBps_`: fee in basis points
27
+
28
+ Examples:
29
+
30
+ - `0`: no protocol fee
31
+ - `30`: 0.30%
32
+ - `50`: 0.50%
33
+
34
+ The contract caps fees at `500` basis points, which is 5%.
35
+
36
+ ## After Deployment
37
+
38
+ Add the deployed contract address to the app:
39
+
40
+ ```bash
41
+ NEXT_PUBLIC_LOKA_LEDGER_ADDRESS=0xDDBc0b6fB1fB0AAaE4321d69B3625ba4CaB2a952
42
+ ```
43
+
44
+ Then add the same contract address to the project profile on Talent App.
45
+
46
+ Current Celo Mainnet ledger:
47
+
48
+ ```text
49
+ 0xDDBc0b6fB1fB0AAaE4321d69B3625ba4CaB2a952
50
+ ```
51
+
52
+ ## Mainnet Defaults
53
+
54
+ Celo Mainnet:
55
+
56
+ - Chain ID: `42220`
57
+ - RPC: `https://forno.celo.org`
58
+ - Explorer: `https://celoscan.io`
59
+
60
+ USDm on Celo Mainnet:
61
+
62
+ ```text
63
+ 0x765DE816845861e75A25fCA122bb6898B8B1282a
64
+ ```
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@bamzzstudio/loka-sdk",
3
+ "version": "0.1.0",
4
+ "description": "SDK, contracts, and invoice agent helpers for Loka merchant payments on Celo.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "contracts",
11
+ "docs",
12
+ "README.md"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/adekunlebamz/localpay-sdk.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/adekunlebamz/localpay-sdk/issues"
20
+ },
21
+ "homepage": "https://github.com/adekunlebamz/localpay-sdk#readme",
22
+ "license": "MIT",
23
+ "scripts": {
24
+ "build": "npm run build:packages && tsc -p tsconfig.json",
25
+ "build:packages": "npm run build --prefix packages/core && npm run build --prefix packages/contracts && npm run build --prefix packages/agent",
26
+ "typecheck": "npm run typecheck:packages && tsc -p tsconfig.json --noEmit",
27
+ "typecheck:packages": "npm run typecheck --prefix packages/core && npm run typecheck --prefix packages/contracts && npm run typecheck --prefix packages/agent"
28
+ },
29
+ "workspaces": [
30
+ "packages/core",
31
+ "packages/contracts",
32
+ "packages/agent"
33
+ ],
34
+ "dependencies": {
35
+ "@bamzzstudio/loka-agent": "^0.1.0",
36
+ "@bamzzstudio/loka-contracts": "^0.1.0",
37
+ "@bamzzstudio/loka-core": "^0.1.0",
38
+ "viem": "^2.48.8"
39
+ },
40
+ "devDependencies": {
41
+ "typescript": "^5.9.3"
42
+ }
43
+ }