Skip to content

Commit

Permalink
Add flow utils contracts (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
austinkline authored Oct 30, 2023
1 parent 09d70eb commit d808d82
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 2 deletions.
109 changes: 109 additions & 0 deletions contracts/flow-utils/AddressUtils.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import "StringUtils"

pub contract AddressUtils {

pub fun withoutPrefix(_ input: String): String {
var address = input

// get rid of 0x
if address.length > 1 && address.utf8[1] == 120 {
address = address.slice(from: 2, upTo: address.length)
}

// ensure even length
if address.length % 2 == 1 {
address = "0".concat(address)
}
return address
}

pub fun parseUInt64(_ input: AnyStruct): UInt64? {
var stringValue = ""

if let string = input as? String {
stringValue = string
} else if let address = input as? Address {
stringValue = address.toString()
} else if let type = input as? Type {
let parts = StringUtils.split(type.identifier, ".")
if parts.length == 1 {
return nil
}
stringValue = parts[1]
} else {
return nil
}

let address = self.withoutPrefix(stringValue)
let bytes = address.decodeHex()
var r: UInt64 = 0
var length = bytes.length
for byte in bytes {
length = length - 1
r = r + (UInt64(byte) << UInt64(length * 8))
}
return r
}

pub fun parseAddress(_ input: AnyStruct): Address? {
if let parsed = self.parseUInt64(input) {
return Address(parsed)
}
return nil
}

pub fun isValidAddress(_ input: AnyStruct, forNetwork: String): Bool {
let address = self.parseUInt64(input)
if address == nil {
return false
}

let codeWords: {String: UInt64} = {
"MAINNET" : 0,
"TESTNET" : 0x6834ba37b3980209,
"EMULATOR": 0x1cb159857af02018
}

let parityCheckMatrixColumns: [UInt64] = [
0x00001, 0x00002, 0x00004, 0x00008, 0x00010, 0x00020, 0x00040, 0x00080,
0x00100, 0x00200, 0x00400, 0x00800, 0x01000, 0x02000, 0x04000, 0x08000,
0x10000, 0x20000, 0x40000, 0x7328d, 0x6689a, 0x6112f, 0x6084b, 0x433fd,
0x42aab, 0x41951, 0x233ce, 0x22a81, 0x21948, 0x1ef60, 0x1deca, 0x1c639,
0x1bdd8, 0x1a535, 0x194ac, 0x18c46, 0x1632b, 0x1529b, 0x14a43, 0x13184,
0x12942, 0x118c1, 0x0f812, 0x0e027, 0x0d00e, 0x0c83c, 0x0b01d, 0x0a831,
0x0982b, 0x07034, 0x0682a, 0x05819, 0x03807, 0x007d2, 0x00727, 0x0068e,
0x0067c, 0x0059d, 0x004eb, 0x003b4, 0x0036a, 0x002d9, 0x001c7, 0x0003f
]

var parity: UInt64 = 0
var codeWord: UInt64 = codeWords[forNetwork]!
codeWord = codeWord ^ address!

if codeWord == 0 {
return false
}

for column in parityCheckMatrixColumns{
if codeWord & 1 == 1 {
parity = parity ^ column
}
codeWord = codeWord >> 1
}

return parity == 0 && codeWord == 0
}

pub fun getNetworkFromAddress(_ input: AnyStruct): String? {
for network in ["MAINNET", "TESTNET", "EMULATOR"]{
if self.isValidAddress(input, forNetwork: network){
return network
}
}
return nil
}

pub fun currentNetwork(): String {
return self.getNetworkFromAddress(self.account.address)!
}

}
69 changes: 69 additions & 0 deletions contracts/flow-utils/ArrayUtils.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copied from https://github.com/bluesign/flow-utils/blob/dnz/cadence/contracts/ArrayUtils.cdc with minor adjustments
pub contract ArrayUtils {

pub fun rangeFunc(_ start: Int, _ end: Int, _ f: ((Int): Void)) {
var current = start
while current < end {
f(current)
current = current + 1
}
}

pub fun range(_ start: Int, _ end: Int): [Int] {
var res: [Int] = []
self.rangeFunc(start, end, fun (i: Int) {
res.append(i)
})
return res
}

pub fun reverse(_ array: [Int]): [Int] {
var res: [Int] = []
var i: Int = array.length - 1
while i >= 0 {
res.append(array[i])
i = i - 1
}
return res
}

pub fun transform(_ array: &[AnyStruct], _ f : ((AnyStruct): AnyStruct)){
for i in self.range(0, array.length){
array[i] = f(array[i])
}
}

pub fun iterate(_ array: [AnyStruct], _ f : ((AnyStruct): Bool)){
for item in array{
if !f(item){
break
}
}
}

pub fun map(_ array: [AnyStruct], _ f : ((AnyStruct): AnyStruct)) : [AnyStruct] {
var res : [AnyStruct] = []
for item in array{
res.append(f(item))
}
return res
}

pub fun mapStrings(_ array: [String], _ f: ((String) : String) ) : [String] {
var res : [String] = []
for item in array{
res.append(f(item))
}
return res
}

pub fun reduce(_ array: [AnyStruct], _ initial: AnyStruct, _ f : ((AnyStruct, AnyStruct): AnyStruct)) : AnyStruct{
var res: AnyStruct = f(initial, array[0])
for i in self.range(1, array.length){
res = f(res, array[i])
}
return res
}

}
97 changes: 97 additions & 0 deletions contracts/flow-utils/StringUtils.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import "ArrayUtils"

pub contract StringUtils {

pub fun format(_ s: String, _ args: {String:String}): String{
var formatted = s
for key in args.keys{
formatted = StringUtils.replaceAll(formatted, "{".concat(key).concat("}"), args[key]!)
}
return formatted
}

pub fun explode(_ s: String): [String]{
var chars : [String] = []
for i in ArrayUtils.range(0, s.length){
chars.append(s[i].toString())
}
return chars
}

pub fun trimLeft(_ s: String): String{
for i in ArrayUtils.range(0, s.length){
if s[i] != " "{
return s.slice(from: i, upTo: s.length)
}
}
return ""
}

pub fun trim(_ s: String): String{
return self.trimLeft(s)
}

pub fun replaceAll(_ s: String, _ search: String, _ replace: String): String{
return self.join(self.split(s, search), replace)
}

pub fun hasPrefix(_ s: String, _ prefix: String) : Bool{
return s.length >= prefix.length && s.slice(from:0, upTo: prefix.length)==prefix
}

pub fun hasSuffix(_ s: String, _ suffix: String) : Bool{
return s.length >= suffix.length && s.slice(from:s.length-suffix.length, upTo: s.length)==suffix
}

pub fun index(_ s : String, _ substr : String, _ startIndex: Int): Int?{
for i in ArrayUtils.range(startIndex,s.length-substr.length+1){
if s[i]==substr[0] && s.slice(from:i, upTo:i+substr.length) == substr{
return i
}
}
return nil
}

pub fun count(_ s: String, _ substr: String): Int{
var pos = [self.index(s, substr, 0)]
while pos[0]!=nil {
pos.insert(at:0, self.index(s, substr, pos[0]!+pos.length*substr.length+1))
}
return pos.length-1
}

pub fun contains(_ s: String, _ substr: String): Bool {
if let index = self.index(s, substr, 0) {
return true
}
return false
}

pub fun substringUntil(_ s: String, _ until: String, _ startIndex: Int): String{
if let index = self.index( s, until, startIndex){
return s.slice(from:startIndex, upTo: index)
}
return s.slice(from:startIndex, upTo:s.length)
}

pub fun split(_ s: String, _ delimiter: String): [String] {
let segments: [String] = []
var p = 0
while p<=s.length{
var preDelimiter = self.substringUntil(s, delimiter, p)
segments.append(preDelimiter)
p = p + preDelimiter.length + delimiter.length
}
return segments
}

pub fun join(_ strs: [String], _ separator: String): String {
var joinedStr = ""
for s in strs {
joinedStr = joinedStr.concat(s).concat(separator)
}
return joinedStr.slice(from: 0, upTo: joinedStr.length - separator.length)
}


}
1 change: 1 addition & 0 deletions example/contracts/Importer.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import "FlowToken"
import "LostAndFoundHelper"
import "DapperOffersV2"
import "TopShot"
import "AddressUtils"

// This contract doesn't do anything, it's just to show that deployments work
// with this import system
Expand Down
29 changes: 28 additions & 1 deletion flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,30 @@
"testnet": "0x8a5f647e58dde1ee",
"mainnet": "0xb8ea91944fd51c43"
}
},
"ArrayUtils": {
"source": "./contracts/flow-utils/ArrayUtils.cdc",
"aliases": {
"emulator": "0xf8d6e0586b0a20c7",
"testnet": "0x31ad40c07a2a9788",
"mainnet": "0xa340dc0a4ec828ab"
}
},
"StringUtils": {
"source": "./contracts/flow-utils/StringUtils.cdc",
"aliases": {
"emulator": "0xf8d6e0586b0a20c7",
"testnet": "0x31ad40c07a2a9788",
"mainnet": "0xa340dc0a4ec828ab"
}
},
"AddressUtils": {
"source": "./contracts/flow-utils/AddressUtils.cdc",
"aliases": {
"emulator": "0xf8d6e0586b0a20c7",
"testnet": "0x31ad40c07a2a9788",
"mainnet": "0xa340dc0a4ec828ab"
}
}
},
"deployments": {
Expand All @@ -288,7 +312,10 @@
"DapperOffersV2",
"OffersV2",
"TopShot",
"TopShotLocking"
"TopShotLocking",
"ArrayUtils",
"StringUtils",
"AddressUtils"
],
"emulator-ft": [
"FungibleToken",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@flowtyio/flow-contracts",
"version": "0.0.15",
"version": "0.0.16",
"main": "index.json",
"description": "An NPM package for common flow contracts",
"author": "flowtyio",
Expand Down

0 comments on commit d808d82

Please sign in to comment.